forked from I2P_Developers/i2p.i2p
merge of '0279ae34b6386470db3de49cbb7dcbdc5629c575'
and '9100eed7ff963fd8a54ab922b65208cfd5fb15b3'
This commit is contained in:
@@ -264,13 +264,12 @@ Applications:
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/LICENSE-ECLIPSE-1.0.html
|
||||
|
||||
JRobin 1.6.0-1 (jrobin.jar):
|
||||
RRD4J 3.5 (jrobin.jar):
|
||||
Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
Copyright 2011 The RRD4J Authors.
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/LICENSE-LGPLv2.1.txt
|
||||
DeallocationHelper:
|
||||
Copyright (c) 2006-2016 Julien Gouesse
|
||||
See licenses/LICENSE-GPLv2.txt
|
||||
|
||||
Ministreaming Lib (mstreaming.jar):
|
||||
By mihi.
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
cache="../../../build"
|
||||
srcdir="./src"
|
||||
destdir="./build/obj" >
|
||||
<classpath>
|
||||
<pathelement location="../../../core/java/build/i2p.jar" />
|
||||
</classpath>
|
||||
</depend>
|
||||
</target>
|
||||
|
||||
@@ -38,9 +35,6 @@
|
||||
encoding="UTF-8"
|
||||
includes="**/*.java" >
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
<classpath>
|
||||
<pathelement location="../../../core/java/build/i2p.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
@@ -63,8 +57,7 @@
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<jar destfile="./build/jrobin.jar" basedir="./build/obj" includes="**/*.class">
|
||||
<manifest>
|
||||
<attribute name="${manifest.classpath.name}" value="i2p.jar" />
|
||||
<attribute name="Implementation-Version" value="1.6.0-1" />
|
||||
<attribute name="Implementation-Version" value="3.5" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
<attribute name="Base-Revision" value="${workspace.version}" />
|
||||
|
||||
79
apps/jrobin/java/src/com/tomgibara/crinch/hashing/Hash.java
Normal file
79
apps/jrobin/java/src/com/tomgibara/crinch/hashing/Hash.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2010 Tom Gibara
|
||||
*
|
||||
* 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 com.tomgibara.crinch.hashing;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Implementations of this interface can generate one hash value for a given
|
||||
* object. Depending upon the implementation, null values may be supported.
|
||||
* </p>
|
||||
*
|
||||
* @author tomgibara
|
||||
*
|
||||
* @param <T>
|
||||
* the type of objects for which hashes may be generated
|
||||
*/
|
||||
|
||||
public interface Hash<T> {
|
||||
|
||||
HashRange getRange();
|
||||
|
||||
/**
|
||||
* The hash value as a {@link BigInteger}. This method may be useful in
|
||||
* circumstances where the generated hash is too large to be accomodated in
|
||||
* a single primitive value, eg. if cryptographic hashes are being used.
|
||||
*
|
||||
* @param value
|
||||
* the object to be hashed
|
||||
* @return the object's hash code, never null
|
||||
* @throws IllegalArgumentException
|
||||
* if the value cannot be hashed
|
||||
*/
|
||||
|
||||
BigInteger hashAsBigInt(T value) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* The hash value as an int. This method should provide better performance
|
||||
* for integer-ranged hashes. This value is not guaranteed to lie within the
|
||||
* indicated {@link com.tomgibara.crinch.hashing.HashRange}.
|
||||
*
|
||||
* @param value
|
||||
* the object to be hashed
|
||||
* @return the object's hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the value cannot be hashed
|
||||
*/
|
||||
|
||||
int hashAsInt(T value) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* The hash value as a long. This method should provide better performance
|
||||
* for long-ranged hashes. This value is not guaranteed to lie within the
|
||||
* indicated {@link com.tomgibara.crinch.hashing.HashRange}.
|
||||
*
|
||||
* @param value
|
||||
* the object to be hashed
|
||||
* @return the object's hash code
|
||||
* @throws IllegalArgumentException
|
||||
* if the value cannot be hashed
|
||||
*/
|
||||
|
||||
long hashAsLong(T value) throws IllegalArgumentException;
|
||||
|
||||
}
|
||||
136
apps/jrobin/java/src/com/tomgibara/crinch/hashing/HashRange.java
Normal file
136
apps/jrobin/java/src/com/tomgibara/crinch/hashing/HashRange.java
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2010 Tom Gibara
|
||||
*
|
||||
* 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 com.tomgibara.crinch.hashing;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Records the range of values that a hash value may take. Both range values are inclusive.
|
||||
*
|
||||
* @author tomgibara
|
||||
*
|
||||
*/
|
||||
|
||||
//TODO really need to revisit the inclusivity of maximum bound
|
||||
public class HashRange {
|
||||
|
||||
// statics
|
||||
|
||||
private static final BigInteger INT_MINIMUM = BigInteger.valueOf(Integer.MIN_VALUE);
|
||||
private static final BigInteger INT_MAXIMUM = BigInteger.valueOf(Integer.MAX_VALUE);
|
||||
private static final BigInteger LONG_MINIMUM = BigInteger.valueOf(Long.MIN_VALUE);
|
||||
private static final BigInteger LONG_MAXIMUM = BigInteger.valueOf(Long.MAX_VALUE);
|
||||
|
||||
public static final HashRange FULL_INT_RANGE = new HashRange(INT_MINIMUM, INT_MAXIMUM);
|
||||
public static final HashRange POSITIVE_INT_RANGE = new HashRange(BigInteger.ONE, INT_MAXIMUM);
|
||||
public static final HashRange FULL_LONG_RANGE = new HashRange(LONG_MINIMUM, LONG_MAXIMUM);
|
||||
public static final HashRange POSITIVE_LONG_RANGE = new HashRange(BigInteger.ONE, LONG_MAXIMUM);
|
||||
|
||||
// fields
|
||||
|
||||
private final BigInteger minimum;
|
||||
private final BigInteger maximum;
|
||||
private final boolean intBounded;
|
||||
private final boolean longBounded;
|
||||
private BigInteger size = null;
|
||||
private Boolean intSized = null;
|
||||
private Boolean longSized = null;
|
||||
|
||||
// constructors
|
||||
|
||||
public HashRange(BigInteger minimum, BigInteger maximum) {
|
||||
if (minimum == null) throw new IllegalArgumentException();
|
||||
if (maximum == null) throw new IllegalArgumentException();
|
||||
if (minimum.compareTo(maximum) > 0) throw new IllegalArgumentException();
|
||||
this.minimum = minimum;
|
||||
this.maximum = maximum;
|
||||
intBounded = minimum.compareTo(INT_MINIMUM) >= 0 && maximum.compareTo(INT_MAXIMUM) <= 0;
|
||||
longBounded = minimum.compareTo(LONG_MINIMUM) >= 0 && maximum.compareTo(LONG_MAXIMUM) <= 0;
|
||||
// defer size related work - don't want to mem alloc in constructor
|
||||
}
|
||||
|
||||
public HashRange(int minimum, int maximum) {
|
||||
this(BigInteger.valueOf(minimum), BigInteger.valueOf(maximum));
|
||||
}
|
||||
|
||||
public HashRange(long minimum, long maximum) {
|
||||
this(BigInteger.valueOf(minimum), BigInteger.valueOf(maximum));
|
||||
}
|
||||
|
||||
// accessors
|
||||
|
||||
public boolean isZeroBased() {
|
||||
return minimum.signum() == 0;
|
||||
}
|
||||
|
||||
public boolean isIntBounded() {
|
||||
return intBounded;
|
||||
}
|
||||
|
||||
public boolean isLongBounded() {
|
||||
return longBounded;
|
||||
}
|
||||
|
||||
public BigInteger getMinimum() {
|
||||
return minimum;
|
||||
}
|
||||
|
||||
public BigInteger getMaximum() {
|
||||
return maximum;
|
||||
}
|
||||
|
||||
public BigInteger getSize() {
|
||||
return size == null ? size = maximum.subtract(minimum).add(BigInteger.ONE) : size;
|
||||
}
|
||||
|
||||
public boolean isIntSized() {
|
||||
if (intSized == null) intSized = getSize().compareTo(INT_MAXIMUM) <= 0;
|
||||
return intSized;
|
||||
}
|
||||
|
||||
public boolean isLongSized() {
|
||||
if (longSized == null) longSized = getSize().compareTo(LONG_MAXIMUM) <= 0;
|
||||
return longSized;
|
||||
}
|
||||
|
||||
// methods
|
||||
|
||||
public HashRange zeroBased() {
|
||||
return isZeroBased() ? this : new HashRange(BigInteger.ZERO, maximum.subtract(minimum));
|
||||
}
|
||||
|
||||
// object methods
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (!(obj instanceof HashRange)) return false;
|
||||
HashRange that = (HashRange) obj;
|
||||
return this.minimum.equals(that.minimum) && this.maximum.equals(that.maximum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return minimum.hashCode() ^ 7 * maximum.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + minimum + ", " + maximum + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright 2010 Tom Gibara
|
||||
*
|
||||
* 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 com.tomgibara.crinch.hashing;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A "minimal perfect hash" for Strings. After construction with an array of
|
||||
* <em>n</em> unique non-null strings, an instance of this class will return a
|
||||
* unique hash value <em>h</em> (0 <= h < n) for any string <em>s</em> in the
|
||||
* array. A negative has value will typically be returned for a string that is
|
||||
* not in the array.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* However, the supplied array is <em>not</em> retained. This means that the
|
||||
* implementation cannot necessarily confirm that a string is not in the
|
||||
* supplied array. Where this implementation cannot distinguish that a string is
|
||||
* not in the array, a 'valid' hash value may be returned. Under no
|
||||
* circumstances will a hash value be returned that is greater than or equal to
|
||||
* <em>n</em>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <strong>IMPORTANT NOTE:</strong> The array of strings supplied to the
|
||||
* constructor will be mutated: it is re-ordered so that
|
||||
* <code>hash(a[i]) == i</code>. Application code must generally use this
|
||||
* information to map hash values back onto the appropriate string value.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <strong>NOTE:</strong> Good performance of this algorithm is predicated on
|
||||
* string hash values being cached by the <code>String</code> class. Experience
|
||||
* indicates that is is a good assumption.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Tom Gibara
|
||||
*/
|
||||
|
||||
public class PerfectStringHash implements Hash<String> {
|
||||
|
||||
// statics
|
||||
|
||||
/**
|
||||
* Comparator used to order the supplied string array. Hashcodes take
|
||||
* priority, we will do a binary search on those. Otherwise, lengths take
|
||||
* priority over character ordering because the hash algorithm prefers to
|
||||
* compare lengths, it's faster.
|
||||
*/
|
||||
|
||||
private static final Comparator<String> comparator = new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String s1, String s2) {
|
||||
final int h1 = s1.hashCode();
|
||||
final int h2 = s2.hashCode();
|
||||
if (h1 == h2) {
|
||||
final int d = s1.length() - s2.length();
|
||||
return d == 0 ? s1.compareTo(s2) : d;
|
||||
}
|
||||
return h1 < h2 ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds a (typically v. small) decision tree for distinguishing strings
|
||||
* that share the same hash value.
|
||||
*
|
||||
* @param values
|
||||
* the string values to distinguish
|
||||
* @param start
|
||||
* the index from which the values should be read
|
||||
* @param length
|
||||
* the number of string values that need to be distinguished
|
||||
* @param pivots
|
||||
* the array that will hold our decision nodes
|
||||
* @param pivotIndex
|
||||
* the index at which the tree should be written
|
||||
*/
|
||||
private static void generatePivots(String[] values, int start, int length, int[] pivots, int pivotIndex) {
|
||||
final int capacity = Integer.highestOneBit(length - 1) << 1;
|
||||
final int depth = Integer.numberOfTrailingZeros(capacity);
|
||||
pivots[ pivotIndex << 1 ] = depth;
|
||||
pivots[(pivotIndex << 1) + 1] = length;
|
||||
pivotIndex++;
|
||||
//build the array
|
||||
for (int i = 0; i < depth; i++) {
|
||||
int step = capacity >> i;
|
||||
for (int j = (1 << (depth-i-1)) - 1; j < capacity; j += step) {
|
||||
final int part;
|
||||
final int comp;
|
||||
if (j >= length - 1) {
|
||||
part = Integer.MIN_VALUE;
|
||||
comp = 0;
|
||||
} else {
|
||||
final String v1 = values[start + j];
|
||||
final String v2 = values[start + j + 1];
|
||||
final int l1 = v1.length();
|
||||
final int l2 = v2.length();
|
||||
if (l1 == l2) {
|
||||
int tPart = -1;
|
||||
int tComp = -1;
|
||||
for (int k = 0; k < l1; k++) {
|
||||
final char c1 = v1.charAt(k);
|
||||
final char c2 = v2.charAt(k);
|
||||
if (c1 == c2) continue;
|
||||
if (c1 < c2) { //must occur at some point because we have already checked that the two strings are unequal
|
||||
tPart = k;
|
||||
tComp = c1;
|
||||
} else {
|
||||
//shouldn't be possible - we've sorted the strings to avoid this case
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
break;
|
||||
}
|
||||
//check if we've been passed a duplicated value
|
||||
if (tPart == -1) throw new IllegalArgumentException("duplicate value: " + v1);
|
||||
part = tPart;
|
||||
comp = tComp;
|
||||
} else {
|
||||
part = -1;
|
||||
comp = l1;
|
||||
}
|
||||
}
|
||||
pivots[ pivotIndex<<1 ] = part;
|
||||
pivots[(pivotIndex<<1) + 1] = comp;
|
||||
pivotIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fields
|
||||
|
||||
/**
|
||||
* The hashcodes of the supplied strings.
|
||||
*/
|
||||
|
||||
private final int[] hashes;
|
||||
|
||||
/**
|
||||
* Stores two ints for every string, an offset into the pivot array (-1 if
|
||||
* not necessary) and the depth of the decision tree that is rooted there.
|
||||
*/
|
||||
|
||||
private final int[] offsets;
|
||||
|
||||
/**
|
||||
* Stores two ints for every decision, the index at which a character
|
||||
* comparison needs to be made, followed by the character value to be
|
||||
* compared against; or -1 to indicate a length comparison, followed by the
|
||||
* length to be compared against.
|
||||
*/
|
||||
|
||||
private final int[] pivots;
|
||||
|
||||
/**
|
||||
* Cache a range object which indicates the range of hash values generated.
|
||||
*/
|
||||
|
||||
private final HashRange range;
|
||||
|
||||
/**
|
||||
* Constructs a minimal perfect string hashing over the supplied strings.
|
||||
*
|
||||
* @param values
|
||||
* an array of unique non-null strings that will be reordered
|
||||
* such that <code>hash(values[i]) == i</code>.
|
||||
*/
|
||||
|
||||
public PerfectStringHash(final String values[]) {
|
||||
final int length = values.length;
|
||||
if (length == 0) throw new IllegalArgumentException("No values supplied");
|
||||
|
||||
final int[] hashes = new int[length];
|
||||
final int[] offsets = new int[2 * length];
|
||||
final int[] runLengths = new int[length];
|
||||
|
||||
//sort values so that we can assume ordering by hashcode, length and char[]
|
||||
Arrays.sort(values, comparator);
|
||||
|
||||
//pull the hashcodes into an array for analysis
|
||||
for (int i = 0; i < length; i++) hashes[i] = values[i].hashCode();
|
||||
|
||||
//test for unique hashes
|
||||
int offset = 0;
|
||||
if (length > 1) {
|
||||
int previousHash = hashes[0];
|
||||
int runLength = 1;
|
||||
for (int i = 1; i <= length; i++) {
|
||||
int currentHash = i == length ? ~previousHash : hashes[i];
|
||||
if (currentHash == previousHash) {
|
||||
runLength++;
|
||||
} else {
|
||||
if (runLength > 1) {
|
||||
final int firstIndex = i - runLength;
|
||||
for (int j = i - 1; j >= firstIndex; j--) {
|
||||
runLengths[j] = runLength;
|
||||
//offset points to the first node in decision tree
|
||||
offsets[ j<<1 ] = offset;
|
||||
//adjustment is number of indices to first duplicate
|
||||
offsets[(j<<1) + 1] = j - firstIndex;
|
||||
}
|
||||
//extra one for recording depth
|
||||
offset += (Integer.highestOneBit(runLength - 1) << 1);
|
||||
runLength = 1;
|
||||
} else {
|
||||
runLengths[i-1] = 1;
|
||||
offsets[(i-1)<<1] = -1;
|
||||
}
|
||||
}
|
||||
previousHash = currentHash;
|
||||
}
|
||||
}
|
||||
|
||||
//shortcut for when all hashes are unique
|
||||
if (offset == 0) {
|
||||
this.hashes = hashes;
|
||||
this.offsets = null;
|
||||
this.pivots = null;
|
||||
this.range = new HashRange(0, length - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
//build the decision trees
|
||||
final int[] pivots = new int[offset * 2];
|
||||
for (int i = 0; i < length;) {
|
||||
final int runLength = runLengths[i];
|
||||
if (runLength > 1) generatePivots(values, i, runLength, pivots, (int) offsets[i << 1]);
|
||||
i += runLength;
|
||||
}
|
||||
|
||||
//setup our state
|
||||
this.pivots = pivots;
|
||||
this.offsets = offsets;
|
||||
this.hashes = hashes;
|
||||
this.range = new HashRange(0, length - 1);
|
||||
}
|
||||
|
||||
// hash generator methods
|
||||
|
||||
@Override
|
||||
public HashRange getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger hashAsBigInt(String value) {
|
||||
return BigInteger.valueOf(hash(value));
|
||||
}
|
||||
|
||||
//TODO decide whether to throw an IAE if -1 is returned from hash
|
||||
@Override
|
||||
public int hashAsInt(String value) {
|
||||
return hash(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long hashAsLong(String value) {
|
||||
return hash(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a hashcode for the supplied string.
|
||||
*
|
||||
* @param value
|
||||
* any string, not null
|
||||
* @return a minimal hashcode for the supplied string, or -1
|
||||
*/
|
||||
|
||||
private int hash(String value) {
|
||||
final int h = value.hashCode();
|
||||
final int index = Arrays.binarySearch(hashes, h);
|
||||
final int[] pivots = this.pivots;
|
||||
if (pivots == null || index < 0) return index;
|
||||
|
||||
final int offset = offsets[index << 1];
|
||||
if (offset == -1) return index;
|
||||
|
||||
final int depth = pivots[(offset << 1) ];
|
||||
final int count = pivots[(offset << 1) + 1];
|
||||
int i = 0;
|
||||
for (int d = 0; d < depth; d++) {
|
||||
final int t = (offset + (1 << d) + i) << 1;
|
||||
final int part = pivots[t ];
|
||||
final int comp = pivots[t + 1];
|
||||
final boolean right;
|
||||
if (part == Integer.MIN_VALUE) { //easy case - no right value
|
||||
right = false;
|
||||
} else if (part == -1) { //compare length
|
||||
right = value.length() > comp;
|
||||
} else { //lengths are equal, compare character
|
||||
right = value.charAt(part) > (char) comp;
|
||||
}
|
||||
i <<= 1;
|
||||
if (right) i++;
|
||||
}
|
||||
return i >= count ? -1 : index + i - offsets[(index << 1) + 1];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,814 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2016 Julien Gouesse This program is free software; you can
|
||||
* redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version. This program is distributed
|
||||
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received
|
||||
* a copy of the GNU General Public License along with this program; if not,
|
||||
* write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
package engine.misc;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.LongBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Helper to deallocate memory on the native heap allocated during the creation
|
||||
* of a direct byte buffer. It supports numerous virtual machines including
|
||||
* OpenJDK, Oracle/Sun Java, Android Dalvik Virtual Machine, Apache Harmony and
|
||||
* GNU Classpath. This class uses the syntax of Java 1.7 but it can work
|
||||
* correctly with Java 1.4 with a very few minor type changes when using the
|
||||
* maps and the collections. It relies on lots of implementation details but
|
||||
* it's robust enough to go on working (except when the implementors
|
||||
* intentionally use a very general class to store the buffers) despite minor
|
||||
* naming changes like those that occurred between Java 1.6 and Java 1.7. It
|
||||
* supports Java 1.9 despite the move of the cleaner from the package sun.misc
|
||||
* to jdk.internal.ref (in the module java.base). N.B: Releasing the native
|
||||
* memory of a sliced direct NIO buffer, the one of a direct NIO buffer created
|
||||
* with JNI or the one of any direct NIO buffer created by the virtual machine
|
||||
* or by a framework not under your control doesn't prevent the calls to methods
|
||||
* attempting to access such buffers. Those calls can throw an exception or
|
||||
* crash the virtual machine depending on the implementations.
|
||||
*
|
||||
* @author Julien Gouesse
|
||||
*/
|
||||
public class DeallocationHelper {
|
||||
|
||||
private final Log logger = I2PAppContext.getGlobalContext().logManager().getLog(DeallocationHelper.class);
|
||||
|
||||
/**
|
||||
* tool responsible for releasing the native memory of a deallocatable byte
|
||||
* buffer
|
||||
*/
|
||||
public static abstract class Deallocator {
|
||||
|
||||
protected final Log logger = I2PAppContext.getGlobalContext().logManager().getLog(DeallocationHelper.class);
|
||||
|
||||
public Deallocator() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* releases the native memory of a deallocatable byte buffer
|
||||
*
|
||||
* @param directByteBuffer
|
||||
* deallocatable byte buffer
|
||||
*
|
||||
* @return <code>true</code> if the deallocation is successful,
|
||||
* otherwise <code>false</code>
|
||||
*/
|
||||
public abstract boolean run(final ByteBuffer directByteBuffer);
|
||||
}
|
||||
|
||||
public static class OracleSunOpenJdkDeallocator extends Deallocator {
|
||||
|
||||
private Method directByteBufferCleanerMethod;
|
||||
|
||||
private Method cleanerCleanMethod;
|
||||
|
||||
public OracleSunOpenJdkDeallocator() {
|
||||
super();
|
||||
try {
|
||||
final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
|
||||
directByteBufferCleanerMethod = directByteBufferClass.getDeclaredMethod("cleaner");
|
||||
/**
|
||||
* The return type is sun.misc.Cleaner in Java <= 1.8,
|
||||
* jdk.internal.ref.Cleaner in Java >= 1.9. Only the latter
|
||||
* implements the Runnable interface.
|
||||
*/
|
||||
final Class<?> cleanerClass = directByteBufferCleanerMethod.getReturnType();
|
||||
if (Runnable.class.isAssignableFrom(cleanerClass)) {
|
||||
cleanerCleanMethod = Runnable.class.getDeclaredMethod("run");
|
||||
} else {
|
||||
cleanerCleanMethod = cleanerClass.getDeclaredMethod("clean");
|
||||
}
|
||||
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||
logger.warn(
|
||||
"The initialization of the deallocator for Oracle Java, Sun Java and OpenJDK has failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(final ByteBuffer directByteBuffer) {
|
||||
boolean success = false;
|
||||
if (directByteBufferCleanerMethod != null && cleanerCleanMethod != null) {
|
||||
final boolean directByteBufferCleanerMethodWasAccessible = directByteBufferCleanerMethod.isAccessible();
|
||||
final boolean cleanerCleanMethodWasAccessible = cleanerCleanMethod.isAccessible();
|
||||
try {
|
||||
// according to the Java documentation, by default, a reflected object is not accessible
|
||||
directByteBufferCleanerMethod.setAccessible(true);
|
||||
final Object cleaner = directByteBufferCleanerMethod.invoke(directByteBuffer);
|
||||
if (cleaner != null) {
|
||||
cleanerCleanMethod.setAccessible(true);
|
||||
cleanerCleanMethod.invoke(cleaner);
|
||||
success = true;
|
||||
}
|
||||
//} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
} catch (IllegalAccessException | RuntimeException | InvocationTargetException e) {
|
||||
// Replaced with RuntimeException for OpenJDK 9b181
|
||||
// throws a java.lang.reflect.InaccessibleObjectException extends RuntimeException which is only in Java 9
|
||||
// WARNING: An illegal reflective access operation has occurred
|
||||
// WARNING: Illegal reflective access by engine.misc.DeallocationHelper (file:/path/to/jrobin.jar) to field java.nio.DirectByteBuffer.att
|
||||
// WARNING: Please consider reporting this to the maintainers of engine.misc.DeallocationHelper
|
||||
// WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
|
||||
// WARNING: All illegal access operations will be denied in a future release
|
||||
// Thread terminated unexpectedly: Shutdown task net.i2p.router.web.StatSummarizer$Shutdown
|
||||
// java.lang.reflect.InaccessibleObjectException: Unable to make public void jdk.internal.ref.Cleaner.clean() accessible: module java.base does not "exports jdk.internal.ref" to unnamed module @381353a0
|
||||
// at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
|
||||
// at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
|
||||
// at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:198)
|
||||
// at java.base/java.lang.reflect.Method.setAccessible(Method.java:192)
|
||||
logger.warn("The deallocation of a direct NIO buffer has failed", e);
|
||||
} finally {
|
||||
directByteBufferCleanerMethod.setAccessible(directByteBufferCleanerMethodWasAccessible);
|
||||
cleanerCleanMethod.setAccessible(cleanerCleanMethodWasAccessible);
|
||||
}
|
||||
}
|
||||
return (success);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AndroidDeallocator extends Deallocator {
|
||||
|
||||
private Method directByteBufferFreeMethod;
|
||||
|
||||
public AndroidDeallocator() {
|
||||
super();
|
||||
try {
|
||||
final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
|
||||
directByteBufferFreeMethod = directByteBufferClass.getDeclaredMethod("free");
|
||||
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||
logger.warn("The initialization of the deallocator for Android has failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(final ByteBuffer directByteBuffer) {
|
||||
boolean success = false;
|
||||
if (directByteBufferFreeMethod != null) {
|
||||
final boolean directByteBufferFreeMethodWasAccessible = directByteBufferFreeMethod.isAccessible();
|
||||
try {
|
||||
directByteBufferFreeMethod.setAccessible(true);
|
||||
directByteBufferFreeMethod.invoke(directByteBuffer);
|
||||
success = true;
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
logger.warn("The deallocation of a direct NIO buffer has failed", e);
|
||||
} finally {
|
||||
directByteBufferFreeMethod.setAccessible(directByteBufferFreeMethodWasAccessible);
|
||||
}
|
||||
}
|
||||
return (success);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GnuClasspathDeallocator extends Deallocator {
|
||||
|
||||
private Method vmDirectByteBufferFreeMethod;
|
||||
|
||||
private Field bufferAddressField;
|
||||
|
||||
public GnuClasspathDeallocator() {
|
||||
super();
|
||||
try {
|
||||
final Class<?> vmDirectByteBufferClass = Class.forName("java.nio.VMDirectByteBuffer");
|
||||
final Class<?> gnuClasspathPointerClass = Class.forName("gnu.classpath.Pointer");
|
||||
vmDirectByteBufferFreeMethod = vmDirectByteBufferClass.getDeclaredMethod("free",
|
||||
gnuClasspathPointerClass);
|
||||
bufferAddressField = Buffer.class.getDeclaredField("address");
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException e) {
|
||||
logger.warn("The initialization of the deallocator for GNU Classpath has failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(final ByteBuffer directByteBuffer) {
|
||||
boolean success = false;
|
||||
if (vmDirectByteBufferFreeMethod != null && bufferAddressField != null) {
|
||||
final boolean bufferAddressFieldWasAccessible = bufferAddressField.isAccessible();
|
||||
final boolean vmDirectByteBufferFreeMethodWasAccessible = vmDirectByteBufferFreeMethod.isAccessible();
|
||||
try {
|
||||
bufferAddressField.setAccessible(true);
|
||||
final Object address = bufferAddressField.get(directByteBuffer);
|
||||
if (address != null) {
|
||||
vmDirectByteBufferFreeMethod.setAccessible(true);
|
||||
vmDirectByteBufferFreeMethod.invoke(null, address);
|
||||
success = true;
|
||||
}
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
logger.warn("The deallocation of a direct NIO buffer has failed", e);
|
||||
} finally {
|
||||
bufferAddressField.setAccessible(bufferAddressFieldWasAccessible);
|
||||
vmDirectByteBufferFreeMethod.setAccessible(vmDirectByteBufferFreeMethodWasAccessible);
|
||||
}
|
||||
}
|
||||
return (success);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApacheHarmonyDeallocator extends Deallocator {
|
||||
|
||||
private Method directByteBufferFreeMethod;
|
||||
|
||||
public ApacheHarmonyDeallocator() {
|
||||
super();
|
||||
try {
|
||||
final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
|
||||
directByteBufferFreeMethod = directByteBufferClass.getDeclaredMethod("free");
|
||||
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||
logger.warn("The initialization of the deallocator for Apache Harmony has failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean run(final ByteBuffer directByteBuffer) {
|
||||
boolean success = false;
|
||||
if (directByteBufferFreeMethod != null) {
|
||||
final boolean directByteBufferFreeMethodWasAccessible = directByteBufferFreeMethod.isAccessible();
|
||||
try {
|
||||
directByteBufferFreeMethod.setAccessible(true);
|
||||
directByteBufferFreeMethod.invoke(directByteBuffer);
|
||||
success = true;
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
logger.warn("The deallocation of a direct NIO buffer has failed", e);
|
||||
} finally {
|
||||
directByteBufferFreeMethod.setAccessible(directByteBufferFreeMethodWasAccessible);
|
||||
}
|
||||
}
|
||||
return (success);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Class<?>, Field> attachmentOrByteBufferFieldMap;
|
||||
|
||||
private Set<Class<?>> deallocatableBufferClassSet;
|
||||
|
||||
private Deallocator deallocator;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public DeallocationHelper() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor
|
||||
*
|
||||
* @param ignoreClassesAndFieldsHints
|
||||
* <code>true</code> if the known implementation details should
|
||||
* be ignored when looking for the classes and the fields used
|
||||
* for the native memory of the direct buffers (they are then
|
||||
* fully recomputed at runtime which is slower but safer),
|
||||
* otherwise <code>false</code>
|
||||
*/
|
||||
public DeallocationHelper(final boolean ignoreClassesAndFieldsHints) {
|
||||
super();
|
||||
final List<Buffer> buffersToDelete = new ArrayList<>();
|
||||
/**
|
||||
* builds the map used to determine the names of the fields containing
|
||||
* the direct byte buffers. The direct read only buffers and the sliced
|
||||
* buffers and the direct buffers for other primitive types than bytes
|
||||
* store their data into some direct byte buffers. Those direct byte
|
||||
* buffers often are the only one accessing directly to the native
|
||||
* memory. That's why it's necessary to find them when a developer
|
||||
* passes a direct NIO buffer. The code below relies on numerous
|
||||
* implementation details found in some classes not available in the
|
||||
* public APIs, it's used to find the fields faster in most of the
|
||||
* cases. The class names haven't changed since Java 1.4 unlike a few
|
||||
* field names.
|
||||
*/
|
||||
final Map<String, String> attachmentOrByteBufferFieldNameMap = new HashMap<>();
|
||||
final String javaVendor = System.getProperty("java.vendor");
|
||||
final String javaVersion = System.getProperty("java.version");
|
||||
if (!ignoreClassesAndFieldsHints) {
|
||||
if (javaVendor.equals("Sun Microsystems Inc.") || javaVendor.equals("Oracle Corporation")) {
|
||||
final String java14to16DirectBufferAttachmentFieldName = "viewedBuffer";
|
||||
final String java17to19DirectBufferAttachmentFieldName = "att";
|
||||
final String byteBufferAsNonByteBufferByteBufferFieldName = "bb";
|
||||
final String[] directBufferClassnames = new String[] { "java.nio.DirectByteBuffer",
|
||||
"java.nio.DirectByteBufferR", "java.nio.DirectCharBufferRS", "java.nio.DirectCharBufferRU",
|
||||
"java.nio.DirectCharBufferS", "java.nio.DirectCharBufferU", "java.nio.DirectDoubleBufferRS",
|
||||
"java.nio.DirectDoubleBufferRU", "java.nio.DirectDoubleBufferS", "java.nio.DirectDoubleBufferU",
|
||||
"java.nio.DirectFloatBufferRS", "java.nio.DirectFloatBufferRU", "java.nio.DirectFloatBufferS",
|
||||
"java.nio.DirectFloatBufferU", "java.nio.DirectIntBufferRS", "java.nio.DirectIntBufferRU",
|
||||
"java.nio.DirectIntBufferS", "java.nio.DirectIntBufferU", "java.nio.DirectLongBufferRS",
|
||||
"java.nio.DirectLongBufferRU", "java.nio.DirectLongBufferS", "java.nio.DirectLongBufferU",
|
||||
"java.nio.DirectShortBufferRS", "java.nio.DirectShortBufferRU", "java.nio.DirectShortBufferS",
|
||||
"java.nio.DirectShortBufferU" };
|
||||
final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.ByteBufferAsCharBufferB",
|
||||
"java.nio.ByteBufferAsCharBufferL", "java.nio.ByteBufferAsCharBufferRB",
|
||||
"java.nio.ByteBufferAsCharBufferRL", "java.nio.ByteBufferAsDoubleBufferB",
|
||||
"java.nio.ByteBufferAsDoubleBufferL", "java.nio.ByteBufferAsDoubleBufferRB",
|
||||
"java.nio.ByteBufferAsDoubleBufferRL", "java.nio.ByteBufferAsFloatBufferB",
|
||||
"java.nio.ByteBufferAsFloatBufferL", "java.nio.ByteBufferAsFloatBufferRB",
|
||||
"java.nio.ByteBufferAsFloatBufferRL", "java.nio.ByteBufferAsIntBufferB",
|
||||
"java.nio.ByteBufferAsIntBufferL", "java.nio.ByteBufferAsIntBufferRB",
|
||||
"java.nio.ByteBufferAsIntBufferRL", "java.nio.ByteBufferAsLongBufferB",
|
||||
"java.nio.ByteBufferAsLongBufferL", "java.nio.ByteBufferAsLongBufferRB",
|
||||
"java.nio.ByteBufferAsLongBufferRL", "java.nio.ByteBufferAsShortBufferB",
|
||||
"java.nio.ByteBufferAsShortBufferL", "java.nio.ByteBufferAsShortBufferRB",
|
||||
"java.nio.ByteBufferAsShortBufferRL" };
|
||||
final String[] javaVersionElements = System.getProperty("java.version").split("\\.");
|
||||
int indexOfEarlyAccessSuffix = javaVersionElements[0].lastIndexOf("-ea");
|
||||
if (indexOfEarlyAccessSuffix != -1) {
|
||||
// drops the "-ea" suffix from the major version number for
|
||||
// an early access build
|
||||
javaVersionElements[0] = javaVersionElements[0].substring(0, indexOfEarlyAccessSuffix);
|
||||
} else {
|
||||
indexOfEarlyAccessSuffix = javaVersionElements[0].lastIndexOf("-internal");
|
||||
if (indexOfEarlyAccessSuffix != -1) {
|
||||
// drops the "-internal" suffix from the major version number for
|
||||
// an early access build (Ubuntu)
|
||||
javaVersionElements[0] = javaVersionElements[0].substring(0, indexOfEarlyAccessSuffix);
|
||||
} else {
|
||||
indexOfEarlyAccessSuffix = javaVersionElements[0].lastIndexOf("-Ubuntu");
|
||||
if (indexOfEarlyAccessSuffix != -1) {
|
||||
// drops the "-Ubuntu suffix from the major version number for
|
||||
// an early access build (Ubuntu)
|
||||
javaVersionElements[0] = javaVersionElements[0].substring(0, indexOfEarlyAccessSuffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
final int major, minor;
|
||||
if (javaVersionElements.length >= 2) {
|
||||
major = Integer.parseInt(javaVersionElements[0]);
|
||||
int min;
|
||||
try {
|
||||
min = Integer.parseInt(javaVersionElements[1]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
min = 7;
|
||||
}
|
||||
minor = min;
|
||||
} else {
|
||||
major = 1;
|
||||
int min;
|
||||
try {
|
||||
min = Integer.parseInt(javaVersionElements[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
min = 7;
|
||||
}
|
||||
minor = min;
|
||||
}
|
||||
final String directBufferAttachmentFieldName;
|
||||
if (minor == 1 && major <= 6)
|
||||
directBufferAttachmentFieldName = java14to16DirectBufferAttachmentFieldName;
|
||||
else
|
||||
directBufferAttachmentFieldName = java17to19DirectBufferAttachmentFieldName;
|
||||
for (final String directBufferClassname : directBufferClassnames)
|
||||
attachmentOrByteBufferFieldNameMap.put(directBufferClassname, directBufferAttachmentFieldName);
|
||||
for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames)
|
||||
attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname,
|
||||
byteBufferAsNonByteBufferByteBufferFieldName);
|
||||
} else if (javaVendor.equals("The Android Project")) {
|
||||
final String byteBufferAsNonByteBufferByteBufferFieldName = "byteBuffer";
|
||||
final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.ByteBufferAsCharBuffer",
|
||||
"java.nio.ByteBufferAsDoubleBuffer", "java.nio.ByteBufferAsFloatBuffer",
|
||||
"java.nio.ByteBufferAsIntBuffer", "java.nio.ByteBufferAsLongBuffer",
|
||||
"java.nio.ByteBufferAsShortBuffer" };
|
||||
for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames)
|
||||
attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname,
|
||||
byteBufferAsNonByteBufferByteBufferFieldName);
|
||||
} else if (/* javaVendor.equals("Apple Inc.")|| */javaVendor.equals("Free Software Foundation, Inc.")) {
|
||||
final String byteBufferAsNonByteBufferByteBufferFieldName = "bb";
|
||||
final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.CharViewBufferImpl",
|
||||
"java.nio.DoubleViewBufferImpl", "java.nio.FloatViewBufferImpl", "java.nio.IntViewBufferImpl",
|
||||
"java.nio.LongViewBufferImpl", "java.nio.ShortViewBufferImpl" };
|
||||
for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames)
|
||||
attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname,
|
||||
byteBufferAsNonByteBufferByteBufferFieldName);
|
||||
} else if (javaVendor.contains("Apache")) {
|
||||
final String byteBufferAsNonByteBufferByteBufferFieldName = "byteBuffer";
|
||||
final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.CharToByteBufferAdapter",
|
||||
"java.nio.DoubleToByteBufferAdapter", "java.nio.FloatToByteBufferAdapter",
|
||||
"java.nio.IntToByteBufferAdapter", "java.nio.LongToByteBufferAdapter",
|
||||
"java.nio.ShortToByteBufferAdapter" };
|
||||
for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames)
|
||||
attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname,
|
||||
byteBufferAsNonByteBufferByteBufferFieldName);
|
||||
} else if (javaVendor.equals("Jeroen Frijters")) {// TODO IKVM
|
||||
} else if (javaVendor.contains("IBM")) {// TODO J9
|
||||
}
|
||||
}
|
||||
// checks if these classes are in the class library
|
||||
if (!attachmentOrByteBufferFieldNameMap.isEmpty()) {
|
||||
final List<String> classnamesToRemove = new ArrayList<>();
|
||||
for (final String classname : attachmentOrByteBufferFieldNameMap.keySet())
|
||||
try {
|
||||
Class.forName(classname);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
classnamesToRemove.add(classname);
|
||||
}
|
||||
for (final String classnameToRemove : classnamesToRemove)
|
||||
attachmentOrByteBufferFieldNameMap.remove(classnameToRemove);
|
||||
}
|
||||
// builds the map used to determine the fields containing the direct
|
||||
// byte buffers
|
||||
attachmentOrByteBufferFieldMap = new HashMap<>();
|
||||
if (!attachmentOrByteBufferFieldNameMap.isEmpty())
|
||||
for (final Entry<String, String> attachmentOrByteBufferFieldNameEntry : attachmentOrByteBufferFieldNameMap
|
||||
.entrySet()) {
|
||||
final String classname = attachmentOrByteBufferFieldNameEntry.getKey();
|
||||
final String fieldname = attachmentOrByteBufferFieldNameEntry.getValue();
|
||||
try {
|
||||
final Class<?> bufferClass = Class.forName(classname);
|
||||
Field bufferField = null;
|
||||
Class<?> bufferIntermediaryClass = bufferClass;
|
||||
final List<Class<?>> intermediaryClassWithoutBufferList = new ArrayList<>();
|
||||
while (bufferIntermediaryClass != null) {
|
||||
try {
|
||||
bufferField = bufferIntermediaryClass.getDeclaredField(fieldname);
|
||||
} catch (NoSuchFieldException nsfe) {
|
||||
if (!bufferIntermediaryClass.equals(Object.class)
|
||||
&& !bufferIntermediaryClass.equals(Buffer.class))
|
||||
intermediaryClassWithoutBufferList.add(bufferIntermediaryClass);
|
||||
}
|
||||
bufferIntermediaryClass = bufferIntermediaryClass.getSuperclass();
|
||||
}
|
||||
if (bufferField == null) {
|
||||
final String superClassesMsg;
|
||||
if (intermediaryClassWithoutBufferList.isEmpty())
|
||||
superClassesMsg = "";
|
||||
else if (intermediaryClassWithoutBufferList.size() == 1)
|
||||
superClassesMsg = " and in its super class "
|
||||
+ intermediaryClassWithoutBufferList.get(0).getName();
|
||||
else {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(" and in its super classes");
|
||||
int classIndex = 0;
|
||||
for (final Class<?> intermediaryClassWithoutBuffer : intermediaryClassWithoutBufferList) {
|
||||
builder.append(' ');
|
||||
builder.append(intermediaryClassWithoutBuffer.getName());
|
||||
if (classIndex < intermediaryClassWithoutBufferList.size() - 1)
|
||||
builder.append(',');
|
||||
classIndex++;
|
||||
}
|
||||
superClassesMsg = builder.toString();
|
||||
}
|
||||
logger.warn("The field " + fieldname + " hasn't been found in the class " + classname
|
||||
+ superClassesMsg);
|
||||
} else {// the field has been found, stores it into the map
|
||||
attachmentOrByteBufferFieldMap.put(bufferClass, bufferField);
|
||||
}
|
||||
} catch (ClassNotFoundException cnfe) {// TODO The Java version
|
||||
// isn't very useful
|
||||
// under
|
||||
// Android as it is
|
||||
// always zero, rather
|
||||
// use
|
||||
// android.os.Build.VERSION.RELEASE
|
||||
// to show something
|
||||
// meaningful supported
|
||||
// since the API level 1
|
||||
final String msg = "The class " + classname
|
||||
+ " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
|
||||
+ " Java version: " + javaVersion;
|
||||
logger.warn(msg, cnfe);
|
||||
}
|
||||
}
|
||||
// if a known implementation has drastically changed or if the current
|
||||
// implementation is unknown
|
||||
if (attachmentOrByteBufferFieldNameMap.isEmpty()) {// detects everything
|
||||
// with the
|
||||
// reflection API
|
||||
// creates all
|
||||
// possible kinds of
|
||||
// direct NIO buffer
|
||||
// that can contain
|
||||
// buffers (sliced
|
||||
// buffers and views)
|
||||
final ByteBuffer slicedBigEndianReadOnlyDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2)
|
||||
.order(ByteOrder.BIG_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice()
|
||||
.asReadOnlyBuffer();
|
||||
final ByteBuffer slicedBigEndianReadWriteDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2)
|
||||
.order(ByteOrder.BIG_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice();
|
||||
final CharBuffer bigEndianReadOnlyDirectCharBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asCharBuffer();
|
||||
final CharBuffer bigEndianReadWriteDirectCharBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asCharBuffer();
|
||||
final DoubleBuffer bigEndianReadOnlyDirectDoubleBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asDoubleBuffer();
|
||||
final DoubleBuffer bigEndianReadWriteDirectDoubleBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asDoubleBuffer();
|
||||
final FloatBuffer bigEndianReadOnlyDirectFloatBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asFloatBuffer();
|
||||
final FloatBuffer bigEndianReadWriteDirectFloatBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asFloatBuffer();
|
||||
final IntBuffer bigEndianReadOnlyDirectIntBuffer = ByteBuffer.allocateDirect(1).order(ByteOrder.BIG_ENDIAN)
|
||||
.asReadOnlyBuffer().asIntBuffer();
|
||||
final IntBuffer bigEndianReadWriteDirectIntBuffer = ByteBuffer.allocateDirect(1).order(ByteOrder.BIG_ENDIAN)
|
||||
.asIntBuffer();
|
||||
final LongBuffer bigEndianReadOnlyDirectLongBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asLongBuffer();
|
||||
final LongBuffer bigEndianReadWriteDirectLongBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asLongBuffer();
|
||||
final ShortBuffer bigEndianReadOnlyDirectShortBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asShortBuffer();
|
||||
final ShortBuffer bigEndianReadWriteDirectShortBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.BIG_ENDIAN).asShortBuffer();
|
||||
final ByteBuffer slicedLittleEndianReadOnlyDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice()
|
||||
.asReadOnlyBuffer();
|
||||
final ByteBuffer slicedLittleEndianReadWriteDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice();
|
||||
final CharBuffer littleEndianReadOnlyDirectCharBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asCharBuffer();
|
||||
final CharBuffer littleEndianReadWriteDirectCharBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asCharBuffer();
|
||||
final DoubleBuffer littleEndianReadOnlyDirectDoubleBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asDoubleBuffer();
|
||||
final DoubleBuffer littleEndianReadWriteDirectDoubleBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer();
|
||||
final FloatBuffer littleEndianReadOnlyDirectFloatBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asFloatBuffer();
|
||||
final FloatBuffer littleEndianReadWriteDirectFloatBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer();
|
||||
final IntBuffer littleEndianReadOnlyDirectIntBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asIntBuffer();
|
||||
final IntBuffer littleEndianReadWriteDirectIntBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
|
||||
final LongBuffer littleEndianReadOnlyDirectLongBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asLongBuffer();
|
||||
final LongBuffer littleEndianReadWriteDirectLongBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asLongBuffer();
|
||||
final ShortBuffer littleEndianReadOnlyDirectShortBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asShortBuffer();
|
||||
final ShortBuffer littleEndianReadWriteDirectShortBuffer = ByteBuffer.allocateDirect(1)
|
||||
.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
final List<Buffer> buffers = new ArrayList<>();
|
||||
buffers.add(slicedBigEndianReadOnlyDirectByteBuffer);
|
||||
buffers.add(slicedBigEndianReadWriteDirectByteBuffer);
|
||||
buffers.add(bigEndianReadOnlyDirectCharBuffer);
|
||||
buffers.add(bigEndianReadWriteDirectCharBuffer);
|
||||
buffers.add(bigEndianReadOnlyDirectDoubleBuffer);
|
||||
buffers.add(bigEndianReadWriteDirectDoubleBuffer);
|
||||
buffers.add(bigEndianReadOnlyDirectFloatBuffer);
|
||||
buffers.add(bigEndianReadWriteDirectFloatBuffer);
|
||||
buffers.add(bigEndianReadOnlyDirectIntBuffer);
|
||||
buffers.add(bigEndianReadWriteDirectIntBuffer);
|
||||
buffers.add(bigEndianReadOnlyDirectLongBuffer);
|
||||
buffers.add(bigEndianReadWriteDirectLongBuffer);
|
||||
buffers.add(bigEndianReadOnlyDirectShortBuffer);
|
||||
buffers.add(bigEndianReadWriteDirectShortBuffer);
|
||||
buffers.add(slicedLittleEndianReadOnlyDirectByteBuffer);
|
||||
buffers.add(slicedLittleEndianReadWriteDirectByteBuffer);
|
||||
buffers.add(littleEndianReadOnlyDirectCharBuffer);
|
||||
buffers.add(littleEndianReadWriteDirectCharBuffer);
|
||||
buffers.add(littleEndianReadOnlyDirectDoubleBuffer);
|
||||
buffers.add(littleEndianReadWriteDirectDoubleBuffer);
|
||||
buffers.add(littleEndianReadOnlyDirectFloatBuffer);
|
||||
buffers.add(littleEndianReadWriteDirectFloatBuffer);
|
||||
buffers.add(littleEndianReadOnlyDirectIntBuffer);
|
||||
buffers.add(littleEndianReadWriteDirectIntBuffer);
|
||||
buffers.add(littleEndianReadOnlyDirectLongBuffer);
|
||||
buffers.add(littleEndianReadWriteDirectLongBuffer);
|
||||
buffers.add(littleEndianReadOnlyDirectShortBuffer);
|
||||
buffers.add(littleEndianReadWriteDirectShortBuffer);
|
||||
// gets the fields to access the contained buffers
|
||||
for (Buffer buffer : buffers) {
|
||||
final Class<?> bufferClass = buffer.getClass();
|
||||
if (!attachmentOrByteBufferFieldMap.containsKey(bufferClass)) {
|
||||
Field bufferField = null;
|
||||
Class<?> bufferIntermediaryClass = bufferClass;
|
||||
while (bufferIntermediaryClass != null && bufferField == null) {
|
||||
for (final Field field : bufferIntermediaryClass.getDeclaredFields()) {
|
||||
final boolean fieldWasAccessible = field.isAccessible();
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
final Object fieldValue = field.get(buffer);
|
||||
if (fieldValue != null && fieldValue instanceof Buffer) {
|
||||
bufferField = field;
|
||||
break;
|
||||
}
|
||||
} catch (IllegalAccessException iae) {
|
||||
logger.warn("Cannot access the field " + field.getName()
|
||||
+ " of the class " + bufferIntermediaryClass.getName(), iae);
|
||||
} finally {
|
||||
field.setAccessible(fieldWasAccessible);
|
||||
}
|
||||
}
|
||||
bufferIntermediaryClass = bufferIntermediaryClass.getSuperclass();
|
||||
}
|
||||
if (bufferField != null)
|
||||
attachmentOrByteBufferFieldMap.put(bufferClass, bufferField);
|
||||
}
|
||||
}
|
||||
// cleans the mess
|
||||
buffersToDelete.addAll(buffers);
|
||||
}
|
||||
// builds the set of classes whose instances can be deallocated
|
||||
deallocatableBufferClassSet = new HashSet<>();
|
||||
if (javaVendor.equals("Sun Microsystems Inc.") || javaVendor.equals("Oracle Corporation")
|
||||
|| javaVendor.equals("The Android Project")) {
|
||||
Class<?> directByteBufferClass = null;
|
||||
final String directByteBufferClassName = "java.nio.DirectByteBuffer";
|
||||
try {
|
||||
directByteBufferClass = Class.forName(directByteBufferClassName);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
final String msg = "The class " + directByteBufferClassName
|
||||
+ " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
|
||||
+ " Java version: " + javaVersion;
|
||||
logger.warn(msg, cnfe);
|
||||
}
|
||||
if (directByteBufferClass != null)
|
||||
deallocatableBufferClassSet.add(directByteBufferClass);
|
||||
} else if (/* javaVendor.equals("Apple Inc.")|| */javaVendor.equals("Free Software Foundation, Inc.")) {
|
||||
Class<?> readOnlyDirectByteBufferClass = null;
|
||||
final String readOnlyDirectByteBufferClassName = "java.nio.DirectByteBufferImpl.ReadOnly";
|
||||
try {
|
||||
readOnlyDirectByteBufferClass = Class.forName(readOnlyDirectByteBufferClassName);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
final String msg = "The class " + readOnlyDirectByteBufferClassName
|
||||
+ " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
|
||||
+ " Java version: " + javaVersion;
|
||||
logger.warn(msg, cnfe);
|
||||
}
|
||||
if (readOnlyDirectByteBufferClass != null)
|
||||
deallocatableBufferClassSet.add(readOnlyDirectByteBufferClass);
|
||||
Class<?> readWriteDirectByteBufferClass = null;
|
||||
final String readWriteDirectByteBufferClassName = "java.nio.DirectByteBufferImpl.ReadWrite";
|
||||
try {
|
||||
readWriteDirectByteBufferClass = Class.forName(readWriteDirectByteBufferClassName);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
final String msg = "The class " + readWriteDirectByteBufferClassName
|
||||
+ " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
|
||||
+ " Java version: " + javaVersion;
|
||||
logger.warn(msg, cnfe);
|
||||
}
|
||||
if (readWriteDirectByteBufferClass != null)
|
||||
deallocatableBufferClassSet.add(readWriteDirectByteBufferClass);
|
||||
} else if (javaVendor.contains("Apache")) {
|
||||
Class<?> readOnlyDirectByteBufferClass = null;
|
||||
final String readOnlyDirectByteBufferClassName = "java.nio.ReadOnlyDirectByteBuffer";
|
||||
try {
|
||||
readOnlyDirectByteBufferClass = Class.forName(readOnlyDirectByteBufferClassName);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
final String msg = "The class " + readOnlyDirectByteBufferClassName
|
||||
+ " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
|
||||
+ " Java version: " + javaVersion;
|
||||
logger.warn(msg, cnfe);
|
||||
}
|
||||
if (readOnlyDirectByteBufferClass != null)
|
||||
deallocatableBufferClassSet.add(readOnlyDirectByteBufferClass);
|
||||
Class<?> readWriteDirectByteBufferClass = null;
|
||||
final String readWriteDirectByteBufferClassName = "java.nio.ReadWriteDirectByteBuffer";
|
||||
try {
|
||||
readWriteDirectByteBufferClass = Class.forName(readWriteDirectByteBufferClassName);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
final String msg = "The class " + readWriteDirectByteBufferClassName
|
||||
+ " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
|
||||
+ " Java version: " + javaVersion;
|
||||
logger.warn(msg, cnfe);
|
||||
}
|
||||
if (readWriteDirectByteBufferClass != null)
|
||||
deallocatableBufferClassSet.add(readWriteDirectByteBufferClass);
|
||||
} else if (javaVendor.equals("Jeroen Frijters")) {// TODO IKVM
|
||||
} else if (javaVendor.contains("IBM")) {// TODO J9
|
||||
}
|
||||
// if there is no known implementation class of the direct byte buffers
|
||||
if (deallocatableBufferClassSet.isEmpty()) {// creates a read write
|
||||
// direct byte buffer
|
||||
final ByteBuffer dummyReadWriteDirectByteBuffer = ByteBuffer.allocateDirect(1);
|
||||
// gets its class
|
||||
final Class<?> readWriteDirectByteBufferClass = dummyReadWriteDirectByteBuffer.getClass();
|
||||
// stores this class
|
||||
deallocatableBufferClassSet.add(readWriteDirectByteBufferClass);
|
||||
// cleans the mess
|
||||
buffersToDelete.add(dummyReadWriteDirectByteBuffer);
|
||||
// creates a read only direct byte buffer
|
||||
final ByteBuffer dummyReadOnlyDirectByteBuffer = ByteBuffer.allocateDirect(1).asReadOnlyBuffer();
|
||||
// gets its class
|
||||
final Class<?> readOnlyDirectByteBufferClass = dummyReadOnlyDirectByteBuffer.getClass();
|
||||
// stores this class
|
||||
deallocatableBufferClassSet.add(readOnlyDirectByteBufferClass);
|
||||
// cleans the mess
|
||||
buffersToDelete.add(dummyReadOnlyDirectByteBuffer);
|
||||
}
|
||||
// builds the deallocator responsible for releasing the native memory of
|
||||
// a deallocatable byte buffer
|
||||
if (javaVendor.equals("Sun Microsystems Inc.") || javaVendor.equals("Oracle Corporation"))
|
||||
deallocator = new OracleSunOpenJdkDeallocator();
|
||||
else if (javaVendor.equals("The Android Project"))
|
||||
deallocator = new AndroidDeallocator();
|
||||
else if (/* javaVendor.equals("Apple Inc.")|| */javaVendor.equals("Free Software Foundation, Inc."))
|
||||
deallocator = new GnuClasspathDeallocator();
|
||||
else if (javaVendor.contains("Apache"))
|
||||
deallocator = new ApacheHarmonyDeallocator();
|
||||
else if (javaVendor.equals("Jeroen Frijters")) {// TODO IKVM
|
||||
deallocator = null;
|
||||
} else if (javaVendor.contains("IBM")) {// TODO J9
|
||||
deallocator = null;
|
||||
} else
|
||||
deallocator = null;
|
||||
// final cleanup
|
||||
for (final Buffer bufferToDelete : buffersToDelete)
|
||||
deallocate(bufferToDelete);
|
||||
}
|
||||
|
||||
public ByteBuffer findDeallocatableBuffer(Buffer buffer) {
|
||||
final ByteBuffer deallocatableDirectByteBuffer;
|
||||
// looks only for the direct buffers
|
||||
if (buffer != null && buffer.isDirect()) {// looks for any contained
|
||||
// buffer in the passed buffer
|
||||
final Class<?> bufferClass = buffer.getClass();
|
||||
final Field attachmentOrByteBufferField = attachmentOrByteBufferFieldMap == null ? null
|
||||
: attachmentOrByteBufferFieldMap.get(bufferClass);
|
||||
final Buffer attachmentBufferOrByteBuffer;
|
||||
if (attachmentOrByteBufferField == null)
|
||||
attachmentBufferOrByteBuffer = null;
|
||||
else {
|
||||
Object attachedObjectOrByteBuffer;
|
||||
final boolean attachedObjectOrByteBufferFieldWasAccessible = attachmentOrByteBufferField.isAccessible();
|
||||
try {
|
||||
attachmentOrByteBufferField.setAccessible(true);
|
||||
attachedObjectOrByteBuffer = attachmentOrByteBufferField.get(buffer);
|
||||
} catch (IllegalArgumentException | IllegalAccessException iae) {
|
||||
attachedObjectOrByteBuffer = null;
|
||||
} finally {
|
||||
attachmentOrByteBufferField.setAccessible(attachedObjectOrByteBufferFieldWasAccessible);
|
||||
}
|
||||
if (attachedObjectOrByteBuffer instanceof Buffer)
|
||||
attachmentBufferOrByteBuffer = (Buffer) attachedObjectOrByteBuffer;
|
||||
else
|
||||
attachmentBufferOrByteBuffer = null;
|
||||
}
|
||||
// if there is no buffer inside the buffer given in input
|
||||
if (attachmentBufferOrByteBuffer == null) {// if it's a direct byte
|
||||
// buffer and if it's an
|
||||
// instance of
|
||||
// a deallocatable buffer
|
||||
// class
|
||||
if (buffer instanceof ByteBuffer && deallocatableBufferClassSet.contains(bufferClass))
|
||||
deallocatableDirectByteBuffer = (ByteBuffer) buffer;
|
||||
else {// it's not a byte buffer or it's not a
|
||||
// deallocatable buffer
|
||||
deallocatableDirectByteBuffer = null;
|
||||
final String bufferClassName = bufferClass.getName();
|
||||
logger.warn("No deallocatable buffer has been found for an instance of the class "
|
||||
+ bufferClassName + " whereas it is a direct NIO buffer");
|
||||
}
|
||||
} else {// the passed buffer contains another buffer, looks for a
|
||||
// deallocatable buffer inside it
|
||||
deallocatableDirectByteBuffer = findDeallocatableBuffer(attachmentBufferOrByteBuffer);
|
||||
}
|
||||
} else {// there is no need to clean the heap based buffers
|
||||
deallocatableDirectByteBuffer = null;
|
||||
}
|
||||
return deallocatableDirectByteBuffer;
|
||||
}
|
||||
|
||||
public void deallocate(final Buffer buffer) {
|
||||
if (deallocator != null) {
|
||||
final ByteBuffer deallocatableBuffer = findDeallocatableBuffer(buffer);
|
||||
if (deallocatableBuffer != null)
|
||||
deallocator.run(deallocatableBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
public Deallocator getDeallocator() {
|
||||
return (deallocator);
|
||||
}
|
||||
|
||||
public void setDeallocator(Deallocator deallocator) {
|
||||
this.deallocator = deallocator;
|
||||
}
|
||||
|
||||
public Map<Class<?>, Field> getAttachmentOrByteBufferFieldMap() {
|
||||
return (attachmentOrByteBufferFieldMap);
|
||||
}
|
||||
|
||||
public void setAttachmentOrByteBufferFieldMap(Map<Class<?>, Field> attachmentOrByteBufferFieldMap) {
|
||||
this.attachmentOrByteBufferFieldMap = attachmentOrByteBufferFieldMap;
|
||||
}
|
||||
|
||||
public Set<Class<?>> getDeallocatableBufferClassSet() {
|
||||
return (deallocatableBufferClassSet);
|
||||
}
|
||||
|
||||
public void setDeallocatableBufferClassSet(Set<Class<?>> deallocatableBufferClassSet) {
|
||||
this.deallocatableBufferClassSet = deallocatableBufferClassSet;
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
/**
|
||||
* Class to represent single archive definition within the RRD.
|
||||
* Archive definition consists of the following four elements:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>consolidation function
|
||||
* <li>X-files factor
|
||||
* <li>number of steps
|
||||
* <li>number of rows.
|
||||
* </ul>
|
||||
* <p>
|
||||
* For the complete explanation of all archive definition parameters, see RRDTool's
|
||||
* <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
|
||||
public class ArcDef implements ConsolFuns {
|
||||
/**
|
||||
* array of valid consolidation function names
|
||||
*/
|
||||
public static final String CONSOL_FUNS[] = {CF_AVERAGE, CF_MAX, CF_MIN, CF_LAST};
|
||||
|
||||
private String consolFun;
|
||||
private double xff;
|
||||
private int steps, rows;
|
||||
|
||||
/**
|
||||
* Creates new archive definition object. This object should be passed as argument to
|
||||
* {@link RrdDef#addArchive(ArcDef) addArchive()} method of
|
||||
* {@link RrdDb RrdDb} object.
|
||||
* <p>
|
||||
* <p>For the complete explanation of all archive definition parameters, see RRDTool's
|
||||
* <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a></p>
|
||||
*
|
||||
* @param consolFun Consolidation function. Allowed values are "AVERAGE", "MIN",
|
||||
* "MAX" and "LAST" (these string constants are conveniently defined in the
|
||||
* {@link ConsolFuns} class).
|
||||
* @param xff X-files factor, between 0 and 1.
|
||||
* @param steps Number of archive steps.
|
||||
* @param rows Number of archive rows.
|
||||
* @throws RrdException Thrown if any parameter has illegal value.
|
||||
*/
|
||||
public ArcDef(final String consolFun, final double xff, final int steps, final int rows) throws RrdException {
|
||||
this.consolFun = consolFun;
|
||||
this.xff = xff;
|
||||
this.steps = steps;
|
||||
this.rows = rows;
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns consolidation function.
|
||||
*
|
||||
* @return Consolidation function.
|
||||
*/
|
||||
public String getConsolFun() {
|
||||
return consolFun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the X-files factor.
|
||||
*
|
||||
* @return X-files factor value.
|
||||
*/
|
||||
public double getXff() {
|
||||
return xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of primary RRD steps which complete a single archive step.
|
||||
*
|
||||
* @return Number of steps.
|
||||
*/
|
||||
public int getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows (aggregated values) stored in the archive.
|
||||
*
|
||||
* @return Number of rows.
|
||||
*/
|
||||
public int getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
private void validate() throws RrdException {
|
||||
if (!isValidConsolFun(consolFun)) {
|
||||
throw new RrdException("Invalid consolidation function specified: " + consolFun);
|
||||
}
|
||||
if (Double.isNaN(xff) || xff < 0.0 || xff >= 1.0) {
|
||||
throw new RrdException("Invalid xff, must be >= 0 and < 1: " + xff);
|
||||
}
|
||||
if (steps < 1 || rows < 2) {
|
||||
throw new RrdException("Invalid steps/rows settings: " + steps + "/" + rows +
|
||||
". Minimal values allowed are steps=1, rows=2");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string representing archive definition (RRDTool format).
|
||||
*
|
||||
* @return String containing all archive definition parameters.
|
||||
*/
|
||||
public String dump() {
|
||||
return "RRA:" + consolFun + ":" + xff + ":" + steps + ":" + rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two archive definitions are equal.
|
||||
* Archive definitions are considered equal if they have the same number of steps
|
||||
* and the same consolidation function. It is not possible to create RRD with two
|
||||
* equal archive definitions.
|
||||
*
|
||||
* @param obj Archive definition to compare with.
|
||||
* @return <code>true</code> if archive definitions are equal,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj instanceof ArcDef) {
|
||||
final ArcDef arcObj = (ArcDef) obj;
|
||||
return consolFun.equals(arcObj.consolFun) && steps == arcObj.steps;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (consolFun.hashCode() + steps) * 53;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if function argument represents valid consolidation function name.
|
||||
*
|
||||
* @param consolFun Consolidation function to be checked
|
||||
* @return <code>true</code> if <code>consolFun</code> is valid consolidation function,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public static boolean isValidConsolFun(final String consolFun) {
|
||||
for (final String cFun : CONSOL_FUNS) {
|
||||
if (cFun.equals(consolFun)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setRows(final int rows) {
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
boolean exactlyEqual(final ArcDef def) {
|
||||
return consolFun.equals(def.consolFun) && xff == def.xff &&
|
||||
steps == def.steps && rows == def.rows;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ArcDef@" + Integer.toHexString(hashCode()) + "[consolFun=" + consolFun + ",xff=" + xff + ",steps=" + steps + ",rows=" + rows + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Class to represent internal RRD archive state for a single datasource. Objects of this
|
||||
* class are never manipulated directly, it's up to JRobin framework to manage
|
||||
* internal arcihve states.<p>
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
public class ArcState implements RrdUpdater {
|
||||
private Archive parentArc;
|
||||
|
||||
private RrdDouble accumValue;
|
||||
private RrdLong nanSteps;
|
||||
|
||||
ArcState(Archive parentArc, boolean shouldInitialize) throws IOException {
|
||||
this.parentArc = parentArc;
|
||||
accumValue = new RrdDouble(this);
|
||||
nanSteps = new RrdLong(this);
|
||||
if (shouldInitialize) {
|
||||
Header header = parentArc.getParentDb().getHeader();
|
||||
long step = header.getStep();
|
||||
long lastUpdateTime = header.getLastUpdateTime();
|
||||
long arcStep = parentArc.getArcStep();
|
||||
long initNanSteps = (Util.normalize(lastUpdateTime, step) -
|
||||
Util.normalize(lastUpdateTime, arcStep)) / step;
|
||||
accumValue.set(Double.NaN);
|
||||
nanSteps.set(initNanSteps);
|
||||
}
|
||||
}
|
||||
|
||||
String dump() throws IOException {
|
||||
return "accumValue:" + accumValue.get() + " nanSteps:" + nanSteps.get() + "\n";
|
||||
}
|
||||
|
||||
void setNanSteps(long value) throws IOException {
|
||||
nanSteps.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of currently accumulated NaN steps.
|
||||
*
|
||||
* @return Number of currently accumulated NaN steps.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public long getNanSteps() throws IOException {
|
||||
return nanSteps.get();
|
||||
}
|
||||
|
||||
void setAccumValue(double value) throws IOException {
|
||||
accumValue.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value accumulated so far.
|
||||
*
|
||||
* @return Accumulated value
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public double getAccumValue() throws IOException {
|
||||
return accumValue.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Archive object to which this ArcState object belongs.
|
||||
*
|
||||
* @return Parent Archive object.
|
||||
*/
|
||||
public Archive getParent() {
|
||||
return parentArc;
|
||||
}
|
||||
|
||||
void appendXml(XmlWriter writer) throws IOException {
|
||||
writer.startTag("ds");
|
||||
writer.writeTag("value", accumValue.get());
|
||||
writer.writeTag("unknown_datapoints", nanSteps.get());
|
||||
writer.closeTag(); // ds
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies object's internal state to another ArcState object.
|
||||
*
|
||||
* @param other New ArcState object to copy state to
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if supplied argument is not an ArcState object
|
||||
*/
|
||||
public void copyStateTo(RrdUpdater other) throws IOException, RrdException {
|
||||
if (!(other instanceof ArcState)) {
|
||||
throw new RrdException(
|
||||
"Cannot copy ArcState object to " + other.getClass().getName());
|
||||
}
|
||||
ArcState arcState = (ArcState) other;
|
||||
arcState.accumValue.set(accumValue.get());
|
||||
arcState.nanSteps.set(nanSteps.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying storage (backend) object which actually performs all
|
||||
* I/O operations.
|
||||
*
|
||||
* @return I/O backend object
|
||||
*/
|
||||
public RrdBackend getRrdBackend() {
|
||||
return parentArc.getRrdBackend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to implement RrdUpdater interface. You should never call this method directly.
|
||||
*
|
||||
* @return Allocator object
|
||||
*/
|
||||
public RrdAllocator getRrdAllocator() {
|
||||
return parentArc.getRrdAllocator();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ArcState@" + Integer.toHexString(hashCode()) + "[parentArc=" + parentArc + ",accumValue=" + accumValue + ",nanSteps=" + nanSteps + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,418 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Class to represent single RRD archive in a RRD with its internal state.
|
||||
* Normally, you don't need methods to manipulate archive objects directly
|
||||
* because JRobin framework does it automatically for you.
|
||||
* <p>
|
||||
* Each archive object consists of three parts: archive definition, archive state objects
|
||||
* (one state object for each datasource) and round robin archives (one round robin for
|
||||
* each datasource). API (read-only) is provided to access each of theese parts.
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
public class Archive implements RrdUpdater, ConsolFuns {
|
||||
private RrdDb parentDb;
|
||||
// definition
|
||||
private RrdString consolFun;
|
||||
private RrdDouble xff;
|
||||
private RrdInt steps, rows;
|
||||
// state
|
||||
private Robin[] robins;
|
||||
private ArcState[] states;
|
||||
|
||||
Archive(final RrdDb parentDb, final ArcDef arcDef) throws IOException {
|
||||
final boolean shouldInitialize = arcDef != null;
|
||||
this.parentDb = parentDb;
|
||||
consolFun = new RrdString(this, true); // constant, may be cached
|
||||
xff = new RrdDouble(this);
|
||||
steps = new RrdInt(this, true); // constant, may be cached
|
||||
rows = new RrdInt(this, true); // constant, may be cached
|
||||
if (shouldInitialize) {
|
||||
consolFun.set(arcDef.getConsolFun());
|
||||
xff.set(arcDef.getXff());
|
||||
steps.set(arcDef.getSteps());
|
||||
rows.set(arcDef.getRows());
|
||||
}
|
||||
final int dsCount = parentDb.getHeader().getDsCount();
|
||||
states = new ArcState[dsCount];
|
||||
robins = new Robin[dsCount];
|
||||
final int numRows = rows.get();
|
||||
for (int i = 0; i < dsCount; i++) {
|
||||
states[i] = new ArcState(this, shouldInitialize);
|
||||
robins[i] = new Robin(this, numRows, shouldInitialize);
|
||||
}
|
||||
}
|
||||
|
||||
// read from XML
|
||||
Archive(final RrdDb parentDb, final DataImporter reader, final int arcIndex) throws IOException, RrdException,RrdException {
|
||||
this(parentDb, new ArcDef(
|
||||
reader.getConsolFun(arcIndex), reader.getXff(arcIndex),
|
||||
reader.getSteps(arcIndex), reader.getRows(arcIndex)));
|
||||
final int dsCount = parentDb.getHeader().getDsCount();
|
||||
for (int i = 0; i < dsCount; i++) {
|
||||
// restore state
|
||||
states[i].setAccumValue(reader.getStateAccumValue(arcIndex, i));
|
||||
states[i].setNanSteps(reader.getStateNanSteps(arcIndex, i));
|
||||
// restore robins
|
||||
double[] values = reader.getValues(arcIndex, i);
|
||||
robins[i].update(values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns archive time step in seconds. Archive step is equal to RRD step
|
||||
* multiplied with the number of archive steps.
|
||||
*
|
||||
* @return Archive time step in seconds
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public long getArcStep() throws IOException {
|
||||
final long step = parentDb.getHeader().getStep();
|
||||
return step * steps.get();
|
||||
}
|
||||
|
||||
String dump() throws IOException {
|
||||
final StringBuffer buffer = new StringBuffer("== ARCHIVE ==\n");
|
||||
buffer.append("RRA:").append(consolFun.get()).append(":").append(xff.get()).append(":").append(steps.get()).
|
||||
append(":").append(rows.get()).append("\n");
|
||||
buffer.append("interval [").append(getStartTime()).append(", ").append(getEndTime()).append("]" + "\n");
|
||||
for (int i = 0; i < robins.length; i++) {
|
||||
buffer.append(states[i].dump());
|
||||
buffer.append(robins[i].dump());
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
RrdDb getParentDb() {
|
||||
return parentDb;
|
||||
}
|
||||
|
||||
public void archive(final int dsIndex, final double value, final long numStepUpdates) throws IOException {
|
||||
final Robin robin = robins[dsIndex];
|
||||
final ArcState state = states[dsIndex];
|
||||
final long step = parentDb.getHeader().getStep();
|
||||
final long lastUpdateTime = parentDb.getHeader().getLastUpdateTime();
|
||||
long updateTime = Util.normalize(lastUpdateTime, step) + step;
|
||||
final long arcStep = getArcStep();
|
||||
final String consolFunString = consolFun.get();
|
||||
final int numSteps = steps.get();
|
||||
final int numRows = rows.get();
|
||||
final double xffValue = xff.get();
|
||||
|
||||
// finish current step
|
||||
long numUpdates = numStepUpdates;
|
||||
while (numUpdates > 0) {
|
||||
accumulate(state, value, consolFunString);
|
||||
numUpdates--;
|
||||
if (updateTime % arcStep == 0) {
|
||||
finalizeStep(state, robin, consolFunString, numSteps, xffValue);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
updateTime += step;
|
||||
}
|
||||
}
|
||||
// update robin in bulk
|
||||
final int bulkUpdateCount = (int) Math.min(numUpdates / numSteps, (long) numRows);
|
||||
robin.bulkStore(value, bulkUpdateCount);
|
||||
// update remaining steps
|
||||
final long remainingUpdates = numUpdates % numSteps;
|
||||
for (long i = 0; i < remainingUpdates; i++) {
|
||||
accumulate(state, value, consolFunString);
|
||||
}
|
||||
}
|
||||
|
||||
private void accumulate(final ArcState state, final double value, String consolFunString) throws IOException {
|
||||
if (Double.isNaN(value)) {
|
||||
state.setNanSteps(state.getNanSteps() + 1);
|
||||
}
|
||||
else {
|
||||
final double accumValue = state.getAccumValue();
|
||||
if (consolFunString.equals(CF_MIN)) {
|
||||
final double minValue = Util.min(accumValue, value);
|
||||
if (minValue != accumValue) {
|
||||
state.setAccumValue(minValue);
|
||||
}
|
||||
}
|
||||
else if (consolFunString.equals(CF_MAX)) {
|
||||
final double maxValue = Util.max(accumValue, value);
|
||||
if (maxValue != accumValue) {
|
||||
state.setAccumValue(maxValue);
|
||||
}
|
||||
}
|
||||
else if (consolFunString.equals(CF_LAST)) {
|
||||
state.setAccumValue(value);
|
||||
}
|
||||
else if (consolFunString.equals(CF_AVERAGE)) {
|
||||
state.setAccumValue(Util.sum(accumValue, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void finalizeStep(final ArcState state, final Robin robin, final String consolFunString, final long numSteps, final double xffValue) throws IOException {
|
||||
final long nanSteps = state.getNanSteps();
|
||||
//double nanPct = (double) nanSteps / (double) arcSteps;
|
||||
double accumValue = state.getAccumValue();
|
||||
if (nanSteps <= xffValue * numSteps && !Double.isNaN(accumValue)) {
|
||||
if (consolFunString.equals(CF_AVERAGE)) {
|
||||
accumValue /= (numSteps - nanSteps);
|
||||
}
|
||||
robin.store(accumValue);
|
||||
} else {
|
||||
robin.store(Double.NaN);
|
||||
}
|
||||
state.setAccumValue(Double.NaN);
|
||||
state.setNanSteps(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns archive consolidation function ("AVERAGE", "MIN", "MAX" or "LAST").
|
||||
*
|
||||
* @return Archive consolidation function.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public String getConsolFun() throws IOException {
|
||||
return consolFun.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns archive X-files factor.
|
||||
*
|
||||
* @return Archive X-files factor (between 0 and 1).
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public double getXff() throws IOException {
|
||||
return xff.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of archive steps.
|
||||
*
|
||||
* @return Number of archive steps.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public int getSteps() throws IOException {
|
||||
return steps.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of archive rows.
|
||||
*
|
||||
* @return Number of archive rows.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public int getRows() throws IOException {
|
||||
return rows.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current starting timestamp. This value is not constant.
|
||||
*
|
||||
* @return Timestamp corresponding to the first archive row
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public long getStartTime() throws IOException {
|
||||
final long endTime = getEndTime();
|
||||
final long arcStep = getArcStep();
|
||||
final long numRows = rows.get();
|
||||
return endTime - (numRows - 1) * arcStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current ending timestamp. This value is not constant.
|
||||
*
|
||||
* @return Timestamp corresponding to the last archive row
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public long getEndTime() throws IOException {
|
||||
final long arcStep = getArcStep();
|
||||
final long lastUpdateTime = parentDb.getHeader().getLastUpdateTime();
|
||||
return Util.normalize(lastUpdateTime, arcStep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying archive state object. Each datasource has its
|
||||
* corresponding ArcState object (archive states are managed independently
|
||||
* for each RRD datasource).
|
||||
*
|
||||
* @param dsIndex Datasource index
|
||||
* @return Underlying archive state object
|
||||
*/
|
||||
public ArcState getArcState(final int dsIndex) {
|
||||
return states[dsIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying round robin archive. Robins are used to store actual
|
||||
* archive values on a per-datasource basis.
|
||||
*
|
||||
* @param dsIndex Index of the datasource in the RRD.
|
||||
* @return Underlying round robin archive for the given datasource.
|
||||
*/
|
||||
public Robin getRobin(final int dsIndex) {
|
||||
return robins[dsIndex];
|
||||
}
|
||||
|
||||
FetchData fetchData(final FetchRequest request) throws IOException, RrdException {
|
||||
final long arcStep = getArcStep();
|
||||
final long fetchStart = Util.normalize(request.getFetchStart(), arcStep);
|
||||
long fetchEnd = Util.normalize(request.getFetchEnd(), arcStep);
|
||||
if (fetchEnd < request.getFetchEnd()) {
|
||||
fetchEnd += arcStep;
|
||||
}
|
||||
final long startTime = getStartTime();
|
||||
final long endTime = getEndTime();
|
||||
String[] dsToFetch = request.getFilter();
|
||||
if (dsToFetch == null) {
|
||||
dsToFetch = parentDb.getDsNames();
|
||||
}
|
||||
final int dsCount = dsToFetch.length;
|
||||
final int ptsCount = (int) ((fetchEnd - fetchStart) / arcStep + 1);
|
||||
final long[] timestamps = new long[ptsCount];
|
||||
final double[][] values = new double[dsCount][ptsCount];
|
||||
final long matchStartTime = Math.max(fetchStart, startTime);
|
||||
final long matchEndTime = Math.min(fetchEnd, endTime);
|
||||
double[][] robinValues = null;
|
||||
if (matchStartTime <= matchEndTime) {
|
||||
// preload robin values
|
||||
final int matchCount = (int) ((matchEndTime - matchStartTime) / arcStep + 1);
|
||||
final int matchStartIndex = (int) ((matchStartTime - startTime) / arcStep);
|
||||
robinValues = new double[dsCount][];
|
||||
for (int i = 0; i < dsCount; i++) {
|
||||
final int dsIndex = parentDb.getDsIndex(dsToFetch[i]);
|
||||
robinValues[i] = robins[dsIndex].getValues(matchStartIndex, matchCount);
|
||||
}
|
||||
}
|
||||
for (int ptIndex = 0; ptIndex < ptsCount; ptIndex++) {
|
||||
final long time = fetchStart + ptIndex * arcStep;
|
||||
timestamps[ptIndex] = time;
|
||||
for (int i = 0; i < dsCount; i++) {
|
||||
double value = Double.NaN;
|
||||
if (time >= matchStartTime && time <= matchEndTime) {
|
||||
// inbound time
|
||||
final int robinValueIndex = (int) ((time - matchStartTime) / arcStep);
|
||||
assert robinValues != null;
|
||||
value = robinValues[i][robinValueIndex];
|
||||
}
|
||||
values[i][ptIndex] = value;
|
||||
}
|
||||
}
|
||||
final FetchData fetchData = new FetchData(this, request);
|
||||
fetchData.setTimestamps(timestamps);
|
||||
fetchData.setValues(values);
|
||||
return fetchData;
|
||||
}
|
||||
|
||||
void appendXml(final XmlWriter writer) throws IOException {
|
||||
writer.startTag("rra");
|
||||
writer.writeTag("cf", consolFun.get());
|
||||
writer.writeComment(getArcStep() + " seconds");
|
||||
writer.writeTag("pdp_per_row", steps.get());
|
||||
writer.writeTag("xff", xff.get());
|
||||
writer.startTag("cdp_prep");
|
||||
for (final ArcState state : states) {
|
||||
state.appendXml(writer);
|
||||
}
|
||||
writer.closeTag(); // cdp_prep
|
||||
writer.startTag("database");
|
||||
final long startTime = getStartTime();
|
||||
for (int i = 0; i < rows.get(); i++) {
|
||||
final long time = startTime + i * getArcStep();
|
||||
writer.writeComment(Util.getDate(time) + " / " + time);
|
||||
writer.startTag("row");
|
||||
for (final Robin robin : robins) {
|
||||
writer.writeTag("v", robin.getValue(i));
|
||||
}
|
||||
writer.closeTag(); // row
|
||||
}
|
||||
writer.closeTag(); // database
|
||||
writer.closeTag(); // rra
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies object's internal state to another Archive object.
|
||||
*
|
||||
* @param other New Archive object to copy state to
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if supplied argument is not an Archive object
|
||||
*/
|
||||
public void copyStateTo(final RrdUpdater other) throws IOException, RrdException {
|
||||
if (!(other instanceof Archive)) {
|
||||
throw new RrdException("Cannot copy Archive object to " + other.getClass().getName());
|
||||
}
|
||||
final Archive arc = (Archive) other;
|
||||
if (!arc.consolFun.get().equals(consolFun.get())) {
|
||||
throw new RrdException("Incompatible consolidation functions");
|
||||
}
|
||||
if (arc.steps.get() != steps.get()) {
|
||||
throw new RrdException("Incompatible number of steps");
|
||||
}
|
||||
final int count = parentDb.getHeader().getDsCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int j = Util.getMatchingDatasourceIndex(parentDb, i, arc.parentDb);
|
||||
if (j >= 0) {
|
||||
states[i].copyStateTo(arc.states[j]);
|
||||
robins[i].copyStateTo(arc.robins[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets X-files factor to a new value.
|
||||
*
|
||||
* @param xff New X-files factor value. Must be >= 0 and < 1.
|
||||
* @throws RrdException Thrown if invalid value is supplied
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public void setXff(final double xff) throws RrdException, IOException {
|
||||
if (xff < 0D || xff >= 1D) {
|
||||
throw new RrdException("Invalid xff supplied (" + xff + "), must be >= 0 and < 1");
|
||||
}
|
||||
this.xff.set(xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying storage (backend) object which actually performs all
|
||||
* I/O operations.
|
||||
*
|
||||
* @return I/O backend object
|
||||
*/
|
||||
public RrdBackend getRrdBackend() {
|
||||
return parentDb.getRrdBackend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to implement RrdUpdater interface. You should never call this method directly.
|
||||
*
|
||||
* @return Allocator object
|
||||
*/
|
||||
public RrdAllocator getRrdAllocator() {
|
||||
return parentDb.getRrdAllocator();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Archive@" + Integer.toHexString(hashCode()) + "[parentDb=" + parentDb + ",consolFun=" + consolFun + ",xff=" + xff + ",steps=" + steps + ",rows=" + rows + ",robins=" + robins + ",states=" + states + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core;
|
||||
|
||||
/**
|
||||
* Simple interface to represent available consolidation functions
|
||||
*/
|
||||
public interface ConsolFuns {
|
||||
/**
|
||||
* Constant to represent AVERAGE consolidation function
|
||||
*/
|
||||
public static final String CF_AVERAGE = "AVERAGE";
|
||||
|
||||
/**
|
||||
* Constant to represent MIN consolidation function
|
||||
*/
|
||||
public static final String CF_MIN = "MIN";
|
||||
|
||||
/**
|
||||
* Constant to represent MAX consolidation function
|
||||
*/
|
||||
public static final String CF_MAX = "MAX";
|
||||
|
||||
/**
|
||||
* Constant to represent LAST consolidation function
|
||||
*/
|
||||
public static final String CF_LAST = "LAST";
|
||||
|
||||
/**
|
||||
* Constant to represent FIRST consolidation function
|
||||
*/
|
||||
public static final String CF_FIRST = "FIRST";
|
||||
|
||||
/**
|
||||
* Constant to represent TOTAL consolidation function
|
||||
*/
|
||||
public static final String CF_TOTAL = "TOTAL";
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
abstract class DataImporter {
|
||||
|
||||
// header
|
||||
abstract String getVersion() throws RrdException, IOException;
|
||||
|
||||
abstract long getLastUpdateTime() throws RrdException, IOException;
|
||||
|
||||
abstract long getStep() throws RrdException, IOException;
|
||||
|
||||
abstract int getDsCount() throws RrdException, IOException;
|
||||
|
||||
abstract int getArcCount() throws RrdException, IOException;
|
||||
|
||||
// datasource
|
||||
abstract String getDsName(int dsIndex) throws RrdException, IOException;
|
||||
|
||||
abstract String getDsType(int dsIndex) throws RrdException, IOException;
|
||||
|
||||
abstract long getHeartbeat(int dsIndex) throws RrdException, IOException;
|
||||
|
||||
abstract double getMinValue(int dsIndex) throws RrdException, IOException;
|
||||
|
||||
abstract double getMaxValue(int dsIndex) throws RrdException, IOException;
|
||||
|
||||
// datasource state
|
||||
abstract double getLastValue(int dsIndex) throws RrdException, IOException;
|
||||
|
||||
abstract double getAccumValue(int dsIndex) throws RrdException, IOException;
|
||||
|
||||
abstract long getNanSeconds(int dsIndex) throws RrdException, IOException;
|
||||
|
||||
// archive
|
||||
abstract String getConsolFun(int arcIndex) throws RrdException, IOException;
|
||||
|
||||
abstract double getXff(int arcIndex) throws RrdException, IOException;
|
||||
|
||||
abstract int getSteps(int arcIndex) throws RrdException, IOException;
|
||||
|
||||
abstract int getRows(int arcIndex) throws RrdException, IOException;
|
||||
|
||||
// archive state
|
||||
abstract double getStateAccumValue(int arcIndex, int dsIndex) throws RrdException, IOException;
|
||||
|
||||
abstract int getStateNanSteps(int arcIndex, int dsIndex) throws RrdException, IOException;
|
||||
|
||||
abstract double[] getValues(int arcIndex, int dsIndex) throws RrdException, IOException,RrdException;
|
||||
|
||||
long getEstimatedSize() throws RrdException, IOException {
|
||||
int dsCount = getDsCount();
|
||||
int arcCount = getArcCount();
|
||||
int rowCount = 0;
|
||||
for (int i = 0; i < arcCount; i++) {
|
||||
rowCount += getRows(i);
|
||||
}
|
||||
return RrdDef.calculateSize(dsCount, arcCount, rowCount);
|
||||
}
|
||||
|
||||
void release() throws RrdException, IOException {
|
||||
// NOP
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,497 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Class to represent single datasource within RRD. Each datasource object holds the
|
||||
* following information: datasource definition (once set, never changed) and
|
||||
* datasource state variables (changed whenever RRD gets updated).
|
||||
* <p>
|
||||
* Normally, you don't need to manipluate Datasource objects directly, it's up to
|
||||
* JRobin framework to do it for you.
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
|
||||
public class Datasource implements RrdUpdater, DsTypes {
|
||||
private static final double MAX_32_BIT = Math.pow(2, 32);
|
||||
private static final double MAX_64_BIT = Math.pow(2, 64);
|
||||
|
||||
private RrdDb parentDb;
|
||||
// definition
|
||||
private RrdString dsName, dsType;
|
||||
private RrdLong heartbeat;
|
||||
private RrdDouble minValue, maxValue;
|
||||
|
||||
// cache
|
||||
private String m_primitiveDsName = null;
|
||||
private String m_primitiveDsType = null;
|
||||
|
||||
// state variables
|
||||
private RrdDouble lastValue;
|
||||
private RrdLong nanSeconds;
|
||||
private RrdDouble accumValue;
|
||||
|
||||
Datasource(final RrdDb parentDb, final DsDef dsDef) throws IOException {
|
||||
boolean shouldInitialize = dsDef != null;
|
||||
this.parentDb = parentDb;
|
||||
dsName = new RrdString(this);
|
||||
dsType = new RrdString(this);
|
||||
heartbeat = new RrdLong(this);
|
||||
minValue = new RrdDouble(this);
|
||||
maxValue = new RrdDouble(this);
|
||||
lastValue = new RrdDouble(this);
|
||||
accumValue = new RrdDouble(this);
|
||||
nanSeconds = new RrdLong(this);
|
||||
if (shouldInitialize) {
|
||||
dsName.set(dsDef.getDsName());
|
||||
m_primitiveDsName = null;
|
||||
dsType.set(dsDef.getDsType());
|
||||
m_primitiveDsType = null;
|
||||
heartbeat.set(dsDef.getHeartbeat());
|
||||
minValue.set(dsDef.getMinValue());
|
||||
maxValue.set(dsDef.getMaxValue());
|
||||
lastValue.set(Double.NaN);
|
||||
accumValue.set(0.0);
|
||||
final Header header = parentDb.getHeader();
|
||||
nanSeconds.set(header.getLastUpdateTime() % header.getStep());
|
||||
}
|
||||
}
|
||||
|
||||
Datasource(final RrdDb parentDb, final DataImporter reader, final int dsIndex) throws IOException, RrdException {
|
||||
this(parentDb, null);
|
||||
dsName.set(reader.getDsName(dsIndex));
|
||||
m_primitiveDsName = null;
|
||||
dsType.set(reader.getDsType(dsIndex));
|
||||
m_primitiveDsType = null;
|
||||
heartbeat.set(reader.getHeartbeat(dsIndex));
|
||||
minValue.set(reader.getMinValue(dsIndex));
|
||||
maxValue.set(reader.getMaxValue(dsIndex));
|
||||
lastValue.set(reader.getLastValue(dsIndex));
|
||||
accumValue.set(reader.getAccumValue(dsIndex));
|
||||
nanSeconds.set(reader.getNanSeconds(dsIndex));
|
||||
}
|
||||
|
||||
String dump() throws IOException {
|
||||
return "== DATASOURCE ==\n" +
|
||||
"DS:" + dsName.get() + ":" + dsType.get() + ":" +
|
||||
heartbeat.get() + ":" + minValue.get() + ":" +
|
||||
maxValue.get() + "\nlastValue:" + lastValue.get() +
|
||||
" nanSeconds:" + nanSeconds.get() +
|
||||
" accumValue:" + accumValue.get() + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns datasource name.
|
||||
*
|
||||
* @return Datasource name
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public String getDsName() throws IOException {
|
||||
if (m_primitiveDsName == null) {
|
||||
m_primitiveDsName = dsName.get();
|
||||
}
|
||||
return m_primitiveDsName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns datasource type (GAUGE, COUNTER, DERIVE, ABSOLUTE).
|
||||
*
|
||||
* @return Datasource type.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public String getDsType() throws IOException {
|
||||
if (m_primitiveDsType == null) {
|
||||
m_primitiveDsType = dsType.get();
|
||||
}
|
||||
return m_primitiveDsType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns datasource heartbeat
|
||||
*
|
||||
* @return Datasource heartbeat
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
|
||||
public long getHeartbeat() throws IOException {
|
||||
return heartbeat.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns mimimal allowed value for this datasource.
|
||||
*
|
||||
* @return Minimal value allowed.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public double getMinValue() throws IOException {
|
||||
return minValue.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns maximal allowed value for this datasource.
|
||||
*
|
||||
* @return Maximal value allowed.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public double getMaxValue() throws IOException {
|
||||
return maxValue.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last known value of the datasource.
|
||||
*
|
||||
* @return Last datasource value.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public double getLastValue() throws IOException {
|
||||
return lastValue.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value this datasource accumulated so far.
|
||||
*
|
||||
* @return Accumulated datasource value.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public double getAccumValue() throws IOException {
|
||||
return accumValue.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of accumulated NaN seconds.
|
||||
*
|
||||
* @return Accumulated NaN seconds.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public long getNanSeconds() throws IOException {
|
||||
return nanSeconds.get();
|
||||
}
|
||||
|
||||
void process(final long newTime, final double newValue) throws IOException, RrdException {
|
||||
final Header header = parentDb.getHeader();
|
||||
final long step = header.getStep();
|
||||
final long oldTime = header.getLastUpdateTime();
|
||||
final long startTime = Util.normalize(oldTime, step);
|
||||
final long endTime = startTime + step;
|
||||
final double oldValue = lastValue.get();
|
||||
final double updateValue = calculateUpdateValue(oldTime, oldValue, newTime, newValue);
|
||||
if (newTime < endTime) {
|
||||
accumulate(oldTime, newTime, updateValue);
|
||||
}
|
||||
else {
|
||||
// should store something
|
||||
final long boundaryTime = Util.normalize(newTime, step);
|
||||
accumulate(oldTime, boundaryTime, updateValue);
|
||||
final double value = calculateTotal(startTime, boundaryTime);
|
||||
// how many updates?
|
||||
final long numSteps = (boundaryTime - endTime) / step + 1L;
|
||||
// ACTION!
|
||||
parentDb.archive(this, value, numSteps);
|
||||
// cleanup
|
||||
nanSeconds.set(0);
|
||||
accumValue.set(0.0);
|
||||
accumulate(boundaryTime, newTime, updateValue);
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateUpdateValue(final long oldTime, final double oldValue, final long newTime, final double newValue) throws IOException {
|
||||
double updateValue = Double.NaN;
|
||||
if (newTime - oldTime <= heartbeat.get()) {
|
||||
final String type = dsType.get();
|
||||
if (type.equals(DT_GAUGE)) {
|
||||
updateValue = newValue;
|
||||
}
|
||||
else if (type.equals(DT_ABSOLUTE)) {
|
||||
if (!Double.isNaN(newValue)) {
|
||||
updateValue = newValue / (newTime - oldTime);
|
||||
}
|
||||
}
|
||||
else if (type.equals(DT_DERIVE)) {
|
||||
if (!Double.isNaN(newValue) && !Double.isNaN(oldValue)) {
|
||||
updateValue = (newValue - oldValue) / (newTime - oldTime);
|
||||
}
|
||||
}
|
||||
else if (type.equals(DT_COUNTER)) {
|
||||
if (!Double.isNaN(newValue) && !Double.isNaN(oldValue)) {
|
||||
double diff = newValue - oldValue;
|
||||
if (diff < 0) {
|
||||
diff += MAX_32_BIT;
|
||||
}
|
||||
if (diff < 0) {
|
||||
diff += MAX_64_BIT - MAX_32_BIT;
|
||||
}
|
||||
if (diff >= 0) {
|
||||
updateValue = diff / (newTime - oldTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Double.isNaN(updateValue)) {
|
||||
final double minVal = minValue.get();
|
||||
final double maxVal = maxValue.get();
|
||||
if (!Double.isNaN(minVal) && updateValue < minVal) {
|
||||
updateValue = Double.NaN;
|
||||
}
|
||||
if (!Double.isNaN(maxVal) && updateValue > maxVal) {
|
||||
updateValue = Double.NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
lastValue.set(newValue);
|
||||
return updateValue;
|
||||
}
|
||||
|
||||
private void accumulate(final long oldTime, final long newTime, final double updateValue) throws IOException {
|
||||
if (Double.isNaN(updateValue)) {
|
||||
nanSeconds.set(nanSeconds.get() + (newTime - oldTime));
|
||||
}
|
||||
else {
|
||||
accumValue.set(accumValue.get() + updateValue * (newTime - oldTime));
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateTotal(final long startTime, final long boundaryTime) throws IOException {
|
||||
double totalValue = Double.NaN;
|
||||
final long validSeconds = boundaryTime - startTime - nanSeconds.get();
|
||||
if (nanSeconds.get() <= heartbeat.get() && validSeconds > 0) {
|
||||
totalValue = accumValue.get() / validSeconds;
|
||||
}
|
||||
// IMPORTANT:
|
||||
// if datasource name ends with "!", we'll send zeros instead of NaNs
|
||||
// this might be handy from time to time
|
||||
if (Double.isNaN(totalValue) && dsName.get().endsWith(DsDef.FORCE_ZEROS_FOR_NANS_SUFFIX)) {
|
||||
totalValue = 0D;
|
||||
}
|
||||
return totalValue;
|
||||
}
|
||||
|
||||
void appendXml(final XmlWriter writer) throws IOException {
|
||||
writer.startTag("ds");
|
||||
writer.writeTag("name", dsName.get());
|
||||
writer.writeTag("type", dsType.get());
|
||||
writer.writeTag("minimal_heartbeat", heartbeat.get());
|
||||
writer.writeTag("min", minValue.get());
|
||||
writer.writeTag("max", maxValue.get());
|
||||
writer.writeComment("PDP Status");
|
||||
writer.writeTag("last_ds", lastValue.get(), "UNKN");
|
||||
writer.writeTag("value", accumValue.get());
|
||||
writer.writeTag("unknown_sec", nanSeconds.get());
|
||||
writer.closeTag(); // ds
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies object's internal state to another Datasource object.
|
||||
*
|
||||
* @param other New Datasource object to copy state to
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if supplied argument is not a Datasource object
|
||||
*/
|
||||
public void copyStateTo(final RrdUpdater other) throws IOException, RrdException {
|
||||
if (!(other instanceof Datasource)) {
|
||||
throw new RrdException("Cannot copy Datasource object to " + other.getClass().getName());
|
||||
}
|
||||
final Datasource datasource = (Datasource) other;
|
||||
if (!datasource.dsName.get().equals(dsName.get())) {
|
||||
throw new RrdException("Incomaptible datasource names");
|
||||
}
|
||||
if (!datasource.dsType.get().equals(dsType.get())) {
|
||||
throw new RrdException("Incomaptible datasource types");
|
||||
}
|
||||
datasource.lastValue.set(lastValue.get());
|
||||
datasource.nanSeconds.set(nanSeconds.get());
|
||||
datasource.accumValue.set(accumValue.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns index of this Datasource object in the RRD.
|
||||
*
|
||||
* @return Datasource index in the RRD.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public int getDsIndex() throws IOException {
|
||||
try {
|
||||
return parentDb.getDsIndex(dsName.get());
|
||||
}
|
||||
catch (final RrdException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets datasource heartbeat to a new value.
|
||||
*
|
||||
* @param heartbeat New heartbeat value
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if invalid (non-positive) heartbeat value is specified.
|
||||
*/
|
||||
public void setHeartbeat(final long heartbeat) throws RrdException, IOException {
|
||||
if (heartbeat < 1L) {
|
||||
throw new RrdException("Invalid heartbeat specified: " + heartbeat);
|
||||
}
|
||||
this.heartbeat.set(heartbeat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets datasource name to a new value
|
||||
*
|
||||
* @param newDsName New datasource name
|
||||
* @throws RrdException Thrown if invalid data source name is specified (name too long, or
|
||||
* name already defined in the RRD
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public void setDsName(final String newDsName) throws RrdException, IOException {
|
||||
if (newDsName.length() > RrdString.STRING_LENGTH) {
|
||||
throw new RrdException("Invalid datasource name specified: " + newDsName);
|
||||
}
|
||||
if (parentDb.containsDs(newDsName)) {
|
||||
throw new RrdException("Datasource already defined in this RRD: " + newDsName);
|
||||
}
|
||||
dsName.set(newDsName);
|
||||
m_primitiveDsName = null;
|
||||
}
|
||||
|
||||
public void setDsType(final String newDsType) throws RrdException, IOException {
|
||||
if (!DsDef.isValidDsType(newDsType)) {
|
||||
throw new RrdException("Invalid datasource type: " + newDsType);
|
||||
}
|
||||
// set datasource type
|
||||
this.dsType.set(newDsType);
|
||||
m_primitiveDsType = null;
|
||||
// reset datasource status
|
||||
lastValue.set(Double.NaN);
|
||||
accumValue.set(0.0);
|
||||
// reset archive status
|
||||
final int dsIndex = parentDb.getDsIndex(dsName.get());
|
||||
final Archive[] archives = parentDb.getArchives();
|
||||
for (final Archive archive : archives) {
|
||||
archive.getArcState(dsIndex).setAccumValue(Double.NaN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets minimum allowed value for this datasource. If <code>filterArchivedValues</code>
|
||||
* argment is set to true, all archived values less then <code>minValue</code> will
|
||||
* be fixed to NaN.
|
||||
*
|
||||
* @param minValue New minimal value. Specify <code>Double.NaN</code> if no minimal
|
||||
* value should be set
|
||||
* @param filterArchivedValues true, if archived datasource values should be fixed;
|
||||
* false, otherwise.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if invalid minValue was supplied (not less then maxValue)
|
||||
*/
|
||||
public void setMinValue(final double minValue, final boolean filterArchivedValues) throws IOException, RrdException {
|
||||
final double maxValue = this.maxValue.get();
|
||||
if (!Double.isNaN(minValue) && !Double.isNaN(maxValue) && minValue >= maxValue) {
|
||||
throw new RrdException("Invalid min/max values: " + minValue + "/" + maxValue);
|
||||
}
|
||||
this.minValue.set(minValue);
|
||||
if (!Double.isNaN(minValue) && filterArchivedValues) {
|
||||
final int dsIndex = getDsIndex();
|
||||
final Archive[] archives = parentDb.getArchives();
|
||||
for (final Archive archive : archives) {
|
||||
archive.getRobin(dsIndex).filterValues(minValue, Double.NaN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets maximum allowed value for this datasource. If <code>filterArchivedValues</code>
|
||||
* argment is set to true, all archived values greater then <code>maxValue</code> will
|
||||
* be fixed to NaN.
|
||||
*
|
||||
* @param maxValue New maximal value. Specify <code>Double.NaN</code> if no max
|
||||
* value should be set.
|
||||
* @param filterArchivedValues true, if archived datasource values should be fixed;
|
||||
* false, otherwise.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if invalid maxValue was supplied (not greater then minValue)
|
||||
*/
|
||||
public void setMaxValue(final double maxValue, final boolean filterArchivedValues) throws IOException, RrdException {
|
||||
final double minValue = this.minValue.get();
|
||||
if (!Double.isNaN(minValue) && !Double.isNaN(maxValue) && minValue >= maxValue) {
|
||||
throw new RrdException("Invalid min/max values: " + minValue + "/" + maxValue);
|
||||
}
|
||||
this.maxValue.set(maxValue);
|
||||
if (!Double.isNaN(maxValue) && filterArchivedValues) {
|
||||
final int dsIndex = getDsIndex();
|
||||
final Archive[] archives = parentDb.getArchives();
|
||||
for (final Archive archive : archives) {
|
||||
archive.getRobin(dsIndex).filterValues(Double.NaN, maxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets min/max values allowed for this datasource. If <code>filterArchivedValues</code>
|
||||
* argment is set to true, all archived values less then <code>minValue</code> or
|
||||
* greater then <code>maxValue</code> will be fixed to NaN.
|
||||
*
|
||||
* @param minValue New minimal value. Specify <code>Double.NaN</code> if no min
|
||||
* value should be set.
|
||||
* @param maxValue New maximal value. Specify <code>Double.NaN</code> if no max
|
||||
* value should be set.
|
||||
* @param filterArchivedValues true, if archived datasource values should be fixed;
|
||||
* false, otherwise.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if invalid min/max values were supplied
|
||||
*/
|
||||
public void setMinMaxValue(final double minValue, final double maxValue, final boolean filterArchivedValues) throws IOException, RrdException {
|
||||
if (!Double.isNaN(minValue) && !Double.isNaN(maxValue) && minValue >= maxValue) {
|
||||
throw new RrdException("Invalid min/max values: " + minValue + "/" + maxValue);
|
||||
}
|
||||
this.minValue.set(minValue);
|
||||
this.maxValue.set(maxValue);
|
||||
if (!(Double.isNaN(minValue) && Double.isNaN(maxValue)) && filterArchivedValues) {
|
||||
final int dsIndex = getDsIndex();
|
||||
final Archive[] archives = parentDb.getArchives();
|
||||
for (final Archive archive : archives) {
|
||||
archive.getRobin(dsIndex).filterValues(minValue, maxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying storage (backend) object which actually performs all
|
||||
* I/O operations.
|
||||
*
|
||||
* @return I/O backend object
|
||||
*/
|
||||
public RrdBackend getRrdBackend() {
|
||||
return parentDb.getRrdBackend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to implement RrdUpdater interface. You should never call this method directly.
|
||||
*
|
||||
* @return Allocator object
|
||||
*/
|
||||
public RrdAllocator getRrdAllocator() {
|
||||
return parentDb.getRrdAllocator();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getClass().getName() + "@" + Integer.toHexString(hashCode()) + "[parentDb=" + parentDb
|
||||
+ ",dsName=" + dsName + ",dsType=" + dsType + ",heartbeat=" + heartbeat
|
||||
+ ",minValue=" + minValue + ",maxValue=" + maxValue + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
/**
|
||||
* Class to represent single data source definition within the RRD.
|
||||
* Datasource definition consists of the following five elements:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>data source name
|
||||
* <li>data soruce type
|
||||
* <li>heartbeat
|
||||
* <li>minimal value
|
||||
* <li>maximal value
|
||||
* </ul>
|
||||
* <p>
|
||||
* For the complete explanation of all source definition parameters, see RRDTool's
|
||||
* <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
public class DsDef implements DsTypes {
|
||||
/**
|
||||
* array of valid source types
|
||||
*/
|
||||
public static final String[] DS_TYPES = {DT_GAUGE, DT_COUNTER, DT_DERIVE, DT_ABSOLUTE};
|
||||
static final String FORCE_ZEROS_FOR_NANS_SUFFIX = "!";
|
||||
|
||||
private String dsName, dsType;
|
||||
private long heartbeat;
|
||||
private double minValue, maxValue;
|
||||
|
||||
/**
|
||||
* Creates new data source definition object. This object should be passed as argument
|
||||
* to {@link RrdDef#addDatasource(DsDef) addDatasource()}
|
||||
* method of {@link RrdDb RrdDb} object.
|
||||
* <p>
|
||||
* For the complete explanation of all source definition parameters, see RRDTool's
|
||||
* <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
|
||||
* <p>
|
||||
* <b>IMPORTANT NOTE:</b> If datasource name ends with '!', corresponding archives will never
|
||||
* store NaNs as datasource values. In that case, NaN datasource values will be silently
|
||||
* replaced with zeros by the framework.
|
||||
*
|
||||
* @param dsName Data source name.
|
||||
* @param dsType Data source type. Valid values are "COUNTER", "GAUGE", "DERIVE"
|
||||
* and "ABSOLUTE" (these string constants are conveniently defined in the
|
||||
* {@link DsTypes} class).
|
||||
* @param heartbeat Hearbeat
|
||||
* @param minValue Minimal value. Use <code>Double.NaN</code> if unknown.
|
||||
* @param maxValue Maximal value. Use <code>Double.NaN</code> if unknown.
|
||||
* @throws RrdException Thrown if any parameter has illegal value.
|
||||
*/
|
||||
public DsDef(final String dsName, final String dsType, final long heartbeat, final double minValue, final double maxValue) throws RrdException {
|
||||
this.dsName = dsName;
|
||||
this.dsType = dsType;
|
||||
this.heartbeat = heartbeat;
|
||||
this.minValue = minValue;
|
||||
this.maxValue = maxValue;
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data source name.
|
||||
*
|
||||
* @return Data source name.
|
||||
*/
|
||||
public String getDsName() {
|
||||
return dsName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns source type.
|
||||
*
|
||||
* @return Source type ("COUNTER", "GAUGE", "DERIVE" or "ABSOLUTE").
|
||||
*/
|
||||
public String getDsType() {
|
||||
return dsType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns source heartbeat.
|
||||
*
|
||||
* @return Source heartbeat.
|
||||
*/
|
||||
public long getHeartbeat() {
|
||||
return heartbeat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns minimal calculated source value.
|
||||
*
|
||||
* @return Minimal value.
|
||||
*/
|
||||
public double getMinValue() {
|
||||
return minValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns maximal calculated source value.
|
||||
*
|
||||
* @return Maximal value.
|
||||
*/
|
||||
public double getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
private void validate() throws RrdException {
|
||||
if (dsName == null) {
|
||||
throw new RrdException("Null datasource name specified");
|
||||
}
|
||||
if (dsName.length() == 0) {
|
||||
throw new RrdException("Datasource name length equal to zero");
|
||||
}
|
||||
if (dsName.length() > RrdPrimitive.STRING_LENGTH) {
|
||||
throw new RrdException("Datasource name [" + dsName + "] to long (" +
|
||||
dsName.length() + " chars found, only " + RrdPrimitive.STRING_LENGTH + " allowed");
|
||||
}
|
||||
if (!isValidDsType(dsType)) {
|
||||
throw new RrdException("Invalid datasource type specified: " + dsType);
|
||||
}
|
||||
if (heartbeat <= 0) {
|
||||
throw new RrdException("Invalid heartbeat, must be positive: " + heartbeat);
|
||||
}
|
||||
if (!Double.isNaN(minValue) && !Double.isNaN(maxValue) && minValue >= maxValue) {
|
||||
throw new RrdException("Invalid min/max values specified: " +
|
||||
minValue + "/" + maxValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if function argument represents valid source type.
|
||||
*
|
||||
* @param dsType Source type to be checked.
|
||||
* @return <code>true</code> if <code>dsType</code> is valid type,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public static boolean isValidDsType(final String dsType) {
|
||||
for (final String type : DS_TYPES) {
|
||||
if (type.equals(dsType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string representing source definition (RRDTool format).
|
||||
*
|
||||
* @return String containing all data source definition parameters.
|
||||
*/
|
||||
public String dump() {
|
||||
return "DS:" + dsName + ":" + dsType + ":" + heartbeat +
|
||||
":" + Util.formatDouble(minValue, "U", false) +
|
||||
":" + Util.formatDouble(maxValue, "U", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two datasource definitions are equal.
|
||||
* Source definitions are treated as equal if they have the same source name.
|
||||
* It is not possible to create RRD with two equal archive definitions.
|
||||
*
|
||||
* @param obj Archive definition to compare with.
|
||||
* @return <code>true</code> if archive definitions are equal,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj instanceof DsDef) {
|
||||
final DsDef dsObj = (DsDef) obj;
|
||||
return dsName.equals(dsObj.dsName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return dsName.hashCode() * 47;
|
||||
}
|
||||
|
||||
boolean exactlyEqual(final DsDef def) {
|
||||
return dsName.equals(def.dsName) && dsType.equals(def.dsType) &&
|
||||
heartbeat == def.heartbeat && Util.equal(minValue, def.minValue) &&
|
||||
Util.equal(maxValue, def.maxValue);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[" + dump() + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core;
|
||||
|
||||
/**
|
||||
* Simple interface to represent available datasource types.
|
||||
*/
|
||||
public interface DsTypes {
|
||||
/**
|
||||
* Constant to represent GAUGE datasource type
|
||||
*/
|
||||
public static final String DT_GAUGE = "GAUGE";
|
||||
|
||||
/**
|
||||
* Constant to represent COUNTER datasource type
|
||||
*/
|
||||
public static final String DT_COUNTER = "COUNTER";
|
||||
|
||||
/**
|
||||
* Constant to represent DERIVE datasource type
|
||||
*/
|
||||
public static final String DT_DERIVE = "DERIVE";
|
||||
|
||||
/**
|
||||
* Constant to represent ABSOLUTE datasource type
|
||||
*/
|
||||
public static final String DT_ABSOLUTE = "ABSOLUTE";
|
||||
}
|
||||
@@ -1,529 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import org.jrobin.data.Aggregates;
|
||||
import org.jrobin.data.DataProcessor;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Class used to represent data fetched from the RRD.
|
||||
* Object of this class is created when the method
|
||||
* {@link FetchRequest#fetchData() fetchData()} is
|
||||
* called on a {@link FetchRequest FetchRequest} object.
|
||||
* <p>
|
||||
* Data returned from the RRD is, simply, just one big table filled with
|
||||
* timestamps and corresponding datasource values.
|
||||
* Use {@link #getRowCount() getRowCount()} method to count the number
|
||||
* of returned timestamps (table rows).
|
||||
* <p>
|
||||
* The first table column is filled with timestamps. Time intervals
|
||||
* between consecutive timestamps are guaranteed to be equal. Use
|
||||
* {@link #getTimestamps() getTimestamps()} method to get an array of
|
||||
* timestamps returned.
|
||||
* <p>
|
||||
* Remaining columns are filled with datasource values for the whole timestamp range,
|
||||
* on a column-per-datasource basis. Use {@link #getColumnCount() getColumnCount()} to find
|
||||
* the number of datasources and {@link #getValues(int) getValues(i)} method to obtain
|
||||
* all values for the i-th datasource. Returned datasource values correspond to
|
||||
* the values returned with {@link #getTimestamps() getTimestamps()} method.
|
||||
*/
|
||||
public class FetchData implements ConsolFuns {
|
||||
// anything fuuny will do
|
||||
private static final String RPN_SOURCE_NAME = "WHERE THE SPEECHLES UNITE IN A SILENT ACCORD";
|
||||
|
||||
private FetchRequest request;
|
||||
private String[] dsNames;
|
||||
private long[] timestamps;
|
||||
private double[][] values;
|
||||
|
||||
private Archive matchingArchive;
|
||||
private long arcStep;
|
||||
private long arcEndTime;
|
||||
|
||||
FetchData(Archive matchingArchive, FetchRequest request) throws IOException {
|
||||
this.matchingArchive = matchingArchive;
|
||||
this.arcStep = matchingArchive.getArcStep();
|
||||
this.arcEndTime = matchingArchive.getEndTime();
|
||||
this.dsNames = request.getFilter();
|
||||
if (this.dsNames == null) {
|
||||
this.dsNames = matchingArchive.getParentDb().getDsNames();
|
||||
}
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
void setTimestamps(long[] timestamps) {
|
||||
this.timestamps = timestamps;
|
||||
}
|
||||
|
||||
void setValues(double[][] values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows fetched from the corresponding RRD.
|
||||
* Each row represents datasource values for the specific timestamp.
|
||||
*
|
||||
* @return Number of rows.
|
||||
*/
|
||||
public int getRowCount() {
|
||||
return timestamps.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of columns fetched from the corresponding RRD.
|
||||
* This number is always equal to the number of datasources defined
|
||||
* in the RRD. Each column represents values of a single datasource.
|
||||
*
|
||||
* @return Number of columns (datasources).
|
||||
*/
|
||||
public int getColumnCount() {
|
||||
return dsNames.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of timestamps covering the whole range specified in the
|
||||
* {@link FetchRequest FetchReguest} object.
|
||||
*
|
||||
* @return Array of equidistant timestamps.
|
||||
*/
|
||||
public long[] getTimestamps() {
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the step with which this data was fetched.
|
||||
*
|
||||
* @return Step as long.
|
||||
*/
|
||||
public long getStep() {
|
||||
return timestamps[1] - timestamps[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all archived values for a single datasource.
|
||||
* Returned values correspond to timestamps
|
||||
* returned with {@link #getTimestamps() getTimestamps()} method.
|
||||
*
|
||||
* @param dsIndex Datasource index.
|
||||
* @return Array of single datasource values.
|
||||
*/
|
||||
public double[] getValues(int dsIndex) {
|
||||
return values[dsIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all archived values for all datasources.
|
||||
* Returned values correspond to timestamps
|
||||
* returned with {@link #getTimestamps() getTimestamps()} method.
|
||||
*
|
||||
* @return Two-dimensional aray of all datasource values.
|
||||
*/
|
||||
public double[][] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all archived values for a single datasource.
|
||||
* Returned values correspond to timestamps
|
||||
* returned with {@link #getTimestamps() getTimestamps()} method.
|
||||
*
|
||||
* @param dsName Datasource name.
|
||||
* @return Array of single datasource values.
|
||||
* @throws RrdException Thrown if no matching datasource name is found.
|
||||
*/
|
||||
public double[] getValues(String dsName) throws RrdException {
|
||||
for (int dsIndex = 0; dsIndex < getColumnCount(); dsIndex++) {
|
||||
if (dsName.equals(dsNames[dsIndex])) {
|
||||
return getValues(dsIndex);
|
||||
}
|
||||
}
|
||||
throw new RrdException("Datasource [" + dsName + "] not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of values created by applying RPN expression to the fetched data.
|
||||
* For example, if you have two datasources named <code>x</code> and <code>y</code>
|
||||
* in this FetchData and you want to calculate values for <code>(x+y)/2</code> use something like:
|
||||
* <p>
|
||||
* <code>getRpnValues("x,y,+,2,/");</code>
|
||||
*
|
||||
* @param rpnExpression RRDTool-like RPN expression
|
||||
* @return Calculated values
|
||||
* @throws RrdException Thrown if invalid RPN expression is supplied
|
||||
*/
|
||||
public double[] getRpnValues(String rpnExpression) throws RrdException {
|
||||
DataProcessor dataProcessor = createDataProcessor(rpnExpression);
|
||||
return dataProcessor.getValues(RPN_SOURCE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link FetchRequest FetchRequest} object used to create this FetchData object.
|
||||
*
|
||||
* @return Fetch request object.
|
||||
*/
|
||||
public FetchRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first timestamp in this FetchData object.
|
||||
*
|
||||
* @return The smallest timestamp.
|
||||
*/
|
||||
public long getFirstTimestamp() {
|
||||
return timestamps[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last timestamp in this FecthData object.
|
||||
*
|
||||
* @return The biggest timestamp.
|
||||
*/
|
||||
public long getLastTimestamp() {
|
||||
return timestamps[timestamps.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Archive object which is determined to be the best match for the
|
||||
* timestamps specified in the fetch request. All datasource values are obtained
|
||||
* from round robin archives belonging to this archive.
|
||||
*
|
||||
* @return Matching archive.
|
||||
*/
|
||||
public Archive getMatchingArchive() {
|
||||
return matchingArchive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of datasource names found in the corresponding RRD. If the request
|
||||
* was filtered (data was fetched only for selected datasources), only datasources selected
|
||||
* for fetching are returned.
|
||||
*
|
||||
* @return Array of datasource names.
|
||||
*/
|
||||
public String[] getDsNames() {
|
||||
return dsNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the table index number of a datasource by name. Names are case sensitive.
|
||||
*
|
||||
* @param dsName Name of the datasource for which to find the index.
|
||||
* @return Index number of the datasources in the value table.
|
||||
*/
|
||||
public int getDsIndex(String dsName) {
|
||||
// Let's assume the table of dsNames is always small, so it is not necessary to use a hashmap for lookups
|
||||
for (int i = 0; i < dsNames.length; i++) {
|
||||
if (dsNames[i].equals(dsName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // Datasource not found !
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the content of the whole FetchData object. Useful for debugging.
|
||||
*
|
||||
* @return String containing the contents of this object, for debugging.
|
||||
*/
|
||||
public String dump() {
|
||||
StringBuffer buffer = new StringBuffer("");
|
||||
for (int row = 0; row < getRowCount(); row++) {
|
||||
buffer.append(timestamps[row]);
|
||||
buffer.append(": ");
|
||||
for (int dsIndex = 0; dsIndex < getColumnCount(); dsIndex++) {
|
||||
buffer.append(Util.formatDouble(values[dsIndex][row], true));
|
||||
buffer.append(" ");
|
||||
}
|
||||
buffer.append("\n");
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string representing fetched data in a RRDTool-like form.
|
||||
*
|
||||
* @return Fetched data as a string in a rrdfetch-like output form.
|
||||
*/
|
||||
public String toString() {
|
||||
// print header row
|
||||
StringBuffer buff = new StringBuffer();
|
||||
buff.append(padWithBlanks("", 10));
|
||||
buff.append(" ");
|
||||
for (String dsName : dsNames) {
|
||||
buff.append(padWithBlanks(dsName, 18));
|
||||
}
|
||||
buff.append("\n \n");
|
||||
for (int i = 0; i < timestamps.length; i++) {
|
||||
buff.append(padWithBlanks("" + timestamps[i], 10));
|
||||
buff.append(":");
|
||||
for (int j = 0; j < dsNames.length; j++) {
|
||||
double value = values[j][i];
|
||||
String valueStr = Double.isNaN(value) ? "nan" : Util.formatDouble(value);
|
||||
buff.append(padWithBlanks(valueStr, 18));
|
||||
}
|
||||
buff.append("\n");
|
||||
}
|
||||
return buff.toString();
|
||||
}
|
||||
|
||||
private static String padWithBlanks(String input, int width) {
|
||||
StringBuffer buff = new StringBuffer("");
|
||||
int diff = width - input.length();
|
||||
while (diff-- > 0) {
|
||||
buff.append(' ');
|
||||
}
|
||||
buff.append(input);
|
||||
return buff.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns single aggregated value from the fetched data for a single datasource.
|
||||
*
|
||||
* @param dsName Datasource name
|
||||
* @param consolFun Consolidation function to be applied to fetched datasource values.
|
||||
* Valid consolidation functions are "MIN", "MAX", "LAST", "FIRST", "AVERAGE" and "TOTAL"
|
||||
* (these string constants are conveniently defined in the {@link ConsolFuns} class)
|
||||
* @return MIN, MAX, LAST, FIRST, AVERAGE or TOTAL value calculated from the fetched data
|
||||
* for the given datasource name
|
||||
* @throws RrdException Thrown if the given datasource name cannot be found in fetched data.
|
||||
*/
|
||||
public double getAggregate(String dsName, String consolFun) throws RrdException {
|
||||
DataProcessor dp = createDataProcessor(null);
|
||||
return dp.getAggregate(dsName, consolFun);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns aggregated value from the fetched data for a single datasource.
|
||||
* Before applying aggregation functions, specified RPN expression is applied to fetched data.
|
||||
* For example, if you have a gauge datasource named 'foots' but you want to find the maximum
|
||||
* fetched value in meters use something like:
|
||||
* <p>
|
||||
* <code>getAggregate("foots", "MAX", "foots,0.3048,*");</code>
|
||||
*
|
||||
* @param dsName Datasource name
|
||||
* @param consolFun Consolidation function (MIN, MAX, LAST, FIRST, AVERAGE or TOTAL)
|
||||
* @param rpnExpression RRDTool-like RPN expression
|
||||
* @return Aggregated value
|
||||
* @throws RrdException Thrown if the given datasource name cannot be found in fetched data, or if
|
||||
* invalid RPN expression is supplied
|
||||
* @throws IOException Thrown in case of I/O error (unlikely to happen)
|
||||
* @deprecated This method is preserved just for backward compatibility.
|
||||
*/
|
||||
public double getAggregate(String dsName, String consolFun, String rpnExpression)
|
||||
throws RrdException, IOException {
|
||||
// for backward compatibility
|
||||
rpnExpression = rpnExpression.replaceAll("value", dsName);
|
||||
return getRpnAggregate(rpnExpression, consolFun);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns aggregated value for a set of values calculated by applying an RPN expression to the
|
||||
* fetched data. For example, if you have two datasources named <code>x</code> and <code>y</code>
|
||||
* in this FetchData and you want to calculate MAX value of <code>(x+y)/2</code> use something like:
|
||||
* <p>
|
||||
* <code>getRpnAggregate("x,y,+,2,/", "MAX");</code>
|
||||
*
|
||||
* @param rpnExpression RRDTool-like RPN expression
|
||||
* @param consolFun Consolidation function (MIN, MAX, LAST, FIRST, AVERAGE or TOTAL)
|
||||
* @return Aggregated value
|
||||
* @throws RrdException Thrown if invalid RPN expression is supplied
|
||||
*/
|
||||
public double getRpnAggregate(String rpnExpression, String consolFun) throws RrdException {
|
||||
DataProcessor dataProcessor = createDataProcessor(rpnExpression);
|
||||
return dataProcessor.getAggregate(RPN_SOURCE_NAME, consolFun);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all aggregated values (MIN, MAX, LAST, FIRST, AVERAGE or TOTAL) calculated from the fetched data
|
||||
* for a single datasource.
|
||||
*
|
||||
* @param dsName Datasource name.
|
||||
* @return Simple object containing all aggregated values.
|
||||
* @throws RrdException Thrown if the given datasource name cannot be found in the fetched data.
|
||||
*/
|
||||
public Aggregates getAggregates(String dsName) throws RrdException {
|
||||
DataProcessor dataProcessor = createDataProcessor(null);
|
||||
return dataProcessor.getAggregates(dsName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all aggregated values for a set of values calculated by applying an RPN expression to the
|
||||
* fetched data. For example, if you have two datasources named <code>x</code> and <code>y</code>
|
||||
* in this FetchData and you want to calculate MIN, MAX, LAST, FIRST, AVERAGE and TOTAL value
|
||||
* of <code>(x+y)/2</code> use something like:
|
||||
* <p>
|
||||
* <code>getRpnAggregates("x,y,+,2,/");</code>
|
||||
*
|
||||
* @param rpnExpression RRDTool-like RPN expression
|
||||
* @return Object containing all aggregated values
|
||||
* @throws RrdException Thrown if invalid RPN expression is supplied
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public Aggregates getRpnAggregates(String rpnExpression) throws RrdException, IOException {
|
||||
DataProcessor dataProcessor = createDataProcessor(rpnExpression);
|
||||
return dataProcessor.getAggregates(RPN_SOURCE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by ISPs which charge for bandwidth utilization on a "95th percentile" basis.
|
||||
* <p>
|
||||
* The 95th percentile is the highest source value left when the top 5% of a numerically sorted set
|
||||
* of source data is discarded. It is used as a measure of the peak value used when one discounts
|
||||
* a fair amount for transitory spikes. This makes it markedly different from the average.
|
||||
* <p>
|
||||
* Read more about this topic at:
|
||||
* <a href="http://www.red.net/support/resourcecentre/leasedline/percentile.php">Rednet</a> or<br>
|
||||
* <a href="http://www.bytemark.co.uk/support/tech/95thpercentile.html">Bytemark</a>.
|
||||
*
|
||||
* @param dsName Datasource name
|
||||
* @return 95th percentile of fetched source values
|
||||
* @throws RrdException Thrown if invalid source name is supplied
|
||||
*/
|
||||
public double get95Percentile(String dsName) throws RrdException {
|
||||
DataProcessor dataProcessor = createDataProcessor(null);
|
||||
return dataProcessor.get95Percentile(dsName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #get95Percentile(String)}, but for a set of values calculated with the given
|
||||
* RPN expression.
|
||||
*
|
||||
* @param rpnExpression RRDTool-like RPN expression
|
||||
* @return 95-percentile
|
||||
* @throws RrdException Thrown if invalid RPN expression is supplied
|
||||
*/
|
||||
public double getRpn95Percentile(String rpnExpression) throws RrdException {
|
||||
DataProcessor dataProcessor = createDataProcessor(rpnExpression);
|
||||
return dataProcessor.get95Percentile(RPN_SOURCE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps fetch data to output stream in XML format.
|
||||
*
|
||||
* @param outputStream Output stream to dump fetch data to
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public void exportXml(OutputStream outputStream) throws IOException {
|
||||
XmlWriter writer = new XmlWriter(outputStream);
|
||||
writer.startTag("fetch_data");
|
||||
writer.startTag("request");
|
||||
writer.writeTag("file", request.getParentDb().getPath());
|
||||
writer.writeComment(Util.getDate(request.getFetchStart()));
|
||||
writer.writeTag("start", request.getFetchStart());
|
||||
writer.writeComment(Util.getDate(request.getFetchEnd()));
|
||||
writer.writeTag("end", request.getFetchEnd());
|
||||
writer.writeTag("resolution", request.getResolution());
|
||||
writer.writeTag("cf", request.getConsolFun());
|
||||
writer.closeTag(); // request
|
||||
writer.startTag("datasources");
|
||||
for (String dsName : dsNames) {
|
||||
writer.writeTag("name", dsName);
|
||||
}
|
||||
writer.closeTag(); // datasources
|
||||
writer.startTag("data");
|
||||
for (int i = 0; i < timestamps.length; i++) {
|
||||
writer.startTag("row");
|
||||
writer.writeComment(Util.getDate(timestamps[i]));
|
||||
writer.writeTag("timestamp", timestamps[i]);
|
||||
writer.startTag("values");
|
||||
for (int j = 0; j < dsNames.length; j++) {
|
||||
writer.writeTag("v", values[j][i]);
|
||||
}
|
||||
writer.closeTag(); // values
|
||||
writer.closeTag(); // row
|
||||
}
|
||||
writer.closeTag(); // data
|
||||
writer.closeTag(); // fetch_data
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps fetch data to file in XML format.
|
||||
*
|
||||
* @param filepath Path to destination file
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public void exportXml(String filepath) throws IOException {
|
||||
OutputStream outputStream = null;
|
||||
try {
|
||||
outputStream = new FileOutputStream(filepath);
|
||||
exportXml(outputStream);
|
||||
}
|
||||
finally {
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps fetch data in XML format.
|
||||
*
|
||||
* @return String containing XML formatted fetch data
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public String exportXml() throws IOException {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
exportXml(outputStream);
|
||||
return outputStream.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the step of the corresponding RRA archive
|
||||
*
|
||||
* @return Archive step in seconds
|
||||
*/
|
||||
public long getArcStep() {
|
||||
return arcStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp of the last populated slot in the corresponding RRA archive
|
||||
*
|
||||
* @return Timestamp in seconds
|
||||
*/
|
||||
public long getArcEndTime() {
|
||||
return arcEndTime;
|
||||
}
|
||||
|
||||
private DataProcessor createDataProcessor(String rpnExpression) throws RrdException {
|
||||
DataProcessor dataProcessor = new DataProcessor(request.getFetchStart(), request.getFetchEnd());
|
||||
for (String dsName : dsNames) {
|
||||
dataProcessor.addDatasource(dsName, this);
|
||||
}
|
||||
if (rpnExpression != null) {
|
||||
dataProcessor.addDatasource(RPN_SOURCE_NAME, rpnExpression);
|
||||
try {
|
||||
dataProcessor.processData();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
// highly unlikely, since all datasources have already calculated values
|
||||
throw new RuntimeException("Impossible error: " + ioe);
|
||||
}
|
||||
}
|
||||
return dataProcessor;
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Class to represent fetch request. For the complete explanation of all
|
||||
* fetch parameters consult RRDTool's
|
||||
* <a href="../../../../man/rrdfetch.html" target="man">rrdfetch man page</a>.
|
||||
* <p>
|
||||
* You cannot create <code>FetchRequest</code> directly (no public constructor
|
||||
* is provided). Use {@link RrdDb#createFetchRequest(String, long, long, long)
|
||||
* createFetchRequest()} method of your {@link RrdDb RrdDb} object.
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
public class FetchRequest {
|
||||
private RrdDb parentDb;
|
||||
private String consolFun;
|
||||
private long fetchStart;
|
||||
private long fetchEnd;
|
||||
private long resolution;
|
||||
private String[] filter;
|
||||
|
||||
public FetchRequest(RrdDb parentDb, String consolFun, long fetchStart, long fetchEnd, long resolution) throws RrdException {
|
||||
this.parentDb = parentDb;
|
||||
this.consolFun = consolFun;
|
||||
this.fetchStart = fetchStart;
|
||||
this.fetchEnd = fetchEnd;
|
||||
this.resolution = resolution;
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets request filter in order to fetch data only for
|
||||
* the specified array of datasources (datasource names).
|
||||
* If not set (or set to null), fetched data will
|
||||
* containt values of all datasources defined in the corresponding RRD.
|
||||
* To fetch data only from selected
|
||||
* datasources, specify an array of datasource names as method argument.
|
||||
*
|
||||
* @param filter Array of datsources (datsource names) to fetch data from.
|
||||
*/
|
||||
public void setFilter(String[] filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets request filter in order to fetch data only for
|
||||
* the specified set of datasources (datasource names).
|
||||
* If the filter is not set (or set to null), fetched data will
|
||||
* containt values of all datasources defined in the corresponding RRD.
|
||||
* To fetch data only from selected
|
||||
* datasources, specify a set of datasource names as method argument.
|
||||
*
|
||||
* @param filter Set of datsource names to fetch data for.
|
||||
*/
|
||||
public void setFilter(Set<String> filter) {
|
||||
this.filter = filter.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets request filter in order to fetch data only for
|
||||
* a single datasource (datasource name).
|
||||
* If not set (or set to null), fetched data will
|
||||
* containt values of all datasources defined in the corresponding RRD.
|
||||
* To fetch data for a single datasource only,
|
||||
* specify an array of datasource names as method argument.
|
||||
*
|
||||
* @param filter Array of datsources (datsource names) to fetch data from.
|
||||
*/
|
||||
public void setFilter(String filter) {
|
||||
this.filter = (filter == null) ? null : (new String[] {filter});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns request filter. See {@link #setFilter(String[]) setFilter()} for
|
||||
* complete explanation.
|
||||
*
|
||||
* @return Request filter (array of datasource names), null if not set.
|
||||
*/
|
||||
public String[] getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns consolitation function to be used during the fetch process.
|
||||
*
|
||||
* @return Consolidation function.
|
||||
*/
|
||||
public String getConsolFun() {
|
||||
return consolFun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns starting timestamp to be used for the fetch request.
|
||||
*
|
||||
* @return Starting timstamp in seconds.
|
||||
*/
|
||||
public long getFetchStart() {
|
||||
return fetchStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ending timestamp to be used for the fetch request.
|
||||
*
|
||||
* @return Ending timestamp in seconds.
|
||||
*/
|
||||
public long getFetchEnd() {
|
||||
return fetchEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fetch resolution to be used for the fetch request.
|
||||
*
|
||||
* @return Fetch resolution in seconds.
|
||||
*/
|
||||
public long getResolution() {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
private void validate() throws RrdException {
|
||||
if (!ArcDef.isValidConsolFun(consolFun)) {
|
||||
throw new RrdException("Invalid consolidation function in fetch request: " + consolFun);
|
||||
}
|
||||
if (fetchStart < 0) {
|
||||
throw new RrdException("Invalid start time in fetch request: " + fetchStart);
|
||||
}
|
||||
if (fetchEnd < 0) {
|
||||
throw new RrdException("Invalid end time in fetch request: " + fetchEnd);
|
||||
}
|
||||
if (fetchStart > fetchEnd) {
|
||||
throw new RrdException("Invalid start/end time in fetch request: " + fetchStart +
|
||||
" > " + fetchEnd);
|
||||
}
|
||||
if (resolution <= 0) {
|
||||
throw new RrdException("Invalid resolution in fetch request: " + resolution);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the content of fetch request using the syntax of RRDTool's fetch command.
|
||||
*
|
||||
* @return Fetch request dump.
|
||||
*/
|
||||
public String dump() {
|
||||
return "fetch \"" + parentDb.getRrdBackend().getPath() +
|
||||
"\" " + consolFun + " --start " + fetchStart + " --end " + fetchEnd +
|
||||
(resolution > 1 ? " --resolution " + resolution : "");
|
||||
}
|
||||
|
||||
String getRrdToolCommand() {
|
||||
return dump();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data from the underlying RRD and puts it in a single
|
||||
* {@link FetchData FetchData} object.
|
||||
*
|
||||
* @return FetchData object filled with timestamps and datasource values.
|
||||
* @throws RrdException Thrown in case of JRobin specific error.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public FetchData fetchData() throws RrdException, IOException {
|
||||
return parentDb.fetchData(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying RrdDb object.
|
||||
*
|
||||
* @return RrdDb object used to create this FetchRequest object.
|
||||
*/
|
||||
public RrdDb getParentDb() {
|
||||
return parentDb;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Class to represent RRD header. Header information is mainly static (once set, it
|
||||
* cannot be changed), with the exception of last update time (this value is changed whenever
|
||||
* RRD gets updated).
|
||||
* <p>
|
||||
* Normally, you don't need to manipulate the Header object directly - JRobin framework
|
||||
* does it for you.
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>*
|
||||
*/
|
||||
public class Header implements RrdUpdater {
|
||||
static final int SIGNATURE_LENGTH = 2;
|
||||
static final String SIGNATURE = "JR";
|
||||
|
||||
static final String DEFAULT_SIGNATURE = "JRobin, version 0.1";
|
||||
static final String RRDTOOL_VERSION = "0001";
|
||||
|
||||
private RrdDb parentDb;
|
||||
|
||||
private RrdString signature;
|
||||
private RrdLong step;
|
||||
private RrdInt dsCount, arcCount;
|
||||
private RrdLong lastUpdateTime;
|
||||
private Long m_primitiveStep = null;
|
||||
private Integer m_primitiveDsCount = null;
|
||||
private Integer m_primitiveArcCount = null;
|
||||
|
||||
Header(final RrdDb parentDb, final RrdDef rrdDef) throws IOException {
|
||||
final boolean shouldInitialize = rrdDef != null;
|
||||
this.parentDb = parentDb;
|
||||
signature = new RrdString(this); // NOT constant, may NOT be cached
|
||||
step = new RrdLong(this, true); // constant, may be cached
|
||||
dsCount = new RrdInt(this, true); // constant, may be cached
|
||||
arcCount = new RrdInt(this, true); // constant, may be cached
|
||||
lastUpdateTime = new RrdLong(this);
|
||||
if (shouldInitialize) {
|
||||
signature.set(DEFAULT_SIGNATURE);
|
||||
step.set(rrdDef.getStep());
|
||||
dsCount.set(rrdDef.getDsCount());
|
||||
arcCount.set(rrdDef.getArcCount());
|
||||
lastUpdateTime.set(rrdDef.getStartTime());
|
||||
}
|
||||
}
|
||||
|
||||
Header(final RrdDb parentDb, final DataImporter reader) throws IOException, RrdException {
|
||||
this(parentDb, (RrdDef) null);
|
||||
final String version = reader.getVersion();
|
||||
final int intVersion = Integer.parseInt(version);
|
||||
if (intVersion > 3) {
|
||||
throw new RrdException("Could not unserialize xml version " + version);
|
||||
}
|
||||
signature.set(DEFAULT_SIGNATURE);
|
||||
step.set(reader.getStep());
|
||||
dsCount.set(reader.getDsCount());
|
||||
arcCount.set(reader.getArcCount());
|
||||
lastUpdateTime.set(reader.getLastUpdateTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns RRD signature. Initially, the returned string will be
|
||||
* of the form <b><i>JRobin, version x.x</i></b>. Note: RRD format did not
|
||||
* change since Jrobin 1.0.0 release (and probably never will).
|
||||
*
|
||||
* @return RRD signature
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public String getSignature() throws IOException {
|
||||
return signature.get();
|
||||
}
|
||||
|
||||
public String getInfo() throws IOException {
|
||||
return getSignature().substring(SIGNATURE_LENGTH);
|
||||
}
|
||||
|
||||
public void setInfo(String info) throws IOException {
|
||||
if (info != null && info.length() > 0) {
|
||||
signature.set(SIGNATURE + info);
|
||||
}
|
||||
else {
|
||||
signature.set(SIGNATURE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last update time of the RRD.
|
||||
*
|
||||
* @return Timestamp (Unix epoch, no milliseconds) corresponding to the last update time.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public long getLastUpdateTime() throws IOException {
|
||||
return lastUpdateTime.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns primary RRD time step.
|
||||
*
|
||||
* @return Primary time step in seconds
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public long getStep() throws IOException {
|
||||
if (m_primitiveStep == null) {
|
||||
m_primitiveStep = step.get();
|
||||
}
|
||||
return m_primitiveStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of datasources defined in the RRD.
|
||||
*
|
||||
* @return Number of datasources defined
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public int getDsCount() throws IOException {
|
||||
if (m_primitiveDsCount == null) {
|
||||
m_primitiveDsCount = dsCount.get();
|
||||
}
|
||||
return m_primitiveDsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of archives defined in the RRD.
|
||||
*
|
||||
* @return Number of archives defined
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public int getArcCount() throws IOException {
|
||||
if (m_primitiveArcCount == null) {
|
||||
m_primitiveArcCount = arcCount.get();
|
||||
}
|
||||
return m_primitiveArcCount;
|
||||
}
|
||||
|
||||
public void setLastUpdateTime(final long lastUpdateTime) throws IOException {
|
||||
this.lastUpdateTime.set(lastUpdateTime);
|
||||
}
|
||||
|
||||
String dump() throws IOException {
|
||||
return "== HEADER ==\n" +
|
||||
"signature:" + getSignature() +
|
||||
" lastUpdateTime:" + getLastUpdateTime() +
|
||||
" step:" + getStep() +
|
||||
" dsCount:" + getDsCount() +
|
||||
" arcCount:" + getArcCount() + "\n";
|
||||
}
|
||||
|
||||
void appendXml(XmlWriter writer) throws IOException {
|
||||
writer.writeComment(signature.get());
|
||||
writer.writeTag("version", RRDTOOL_VERSION);
|
||||
writer.writeComment("Seconds");
|
||||
writer.writeTag("step", step.get());
|
||||
writer.writeComment(Util.getDate(lastUpdateTime.get()));
|
||||
writer.writeTag("lastupdate", lastUpdateTime.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies object's internal state to another Header object.
|
||||
*
|
||||
* @param other New Header object to copy state to
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if supplied argument is not a Header object
|
||||
*/
|
||||
public void copyStateTo(final RrdUpdater other) throws IOException, RrdException {
|
||||
if (!(other instanceof Header)) {
|
||||
throw new RrdException( "Cannot copy Header object to " + other.getClass().getName());
|
||||
}
|
||||
final Header header = (Header) other;
|
||||
header.signature.set(signature.get());
|
||||
header.lastUpdateTime.set(lastUpdateTime.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying storage (backend) object which actually performs all
|
||||
* I/O operations.
|
||||
*
|
||||
* @return I/O backend object
|
||||
*/
|
||||
public RrdBackend getRrdBackend() {
|
||||
return parentDb.getRrdBackend();
|
||||
}
|
||||
|
||||
boolean isJRobinHeader() throws IOException {
|
||||
return signature.get().startsWith(SIGNATURE);
|
||||
}
|
||||
|
||||
void validateHeader() throws IOException, RrdException {
|
||||
if (!isJRobinHeader()) {
|
||||
throw new RrdException("Invalid file header. File [" + parentDb.getCanonicalPath() + "] is not a JRobin RRD file");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to implement RrdUpdater interface. You should never call this method directly.
|
||||
*
|
||||
* @return Allocator object
|
||||
*/
|
||||
public RrdAllocator getRrdAllocator() {
|
||||
return parentDb.getRrdAllocator();
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Class to represent archive values for a single datasource. Robin class is the heart of
|
||||
* the so-called "round robin database" concept. Basically, each Robin object is a
|
||||
* fixed length array of double values. Each double value reperesents consolidated, archived
|
||||
* value for the specific timestamp. When the underlying array of double values gets completely
|
||||
* filled, new values will replace the oldest ones.
|
||||
* <p>
|
||||
* Robin object does not hold values in memory - such object could be quite large.
|
||||
* Instead of it, Robin reads them from the backend I/O only when necessary.
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
public class Robin implements RrdUpdater {
|
||||
private Archive parentArc;
|
||||
private RrdInt pointer;
|
||||
private RrdDoubleArray values;
|
||||
private int rows;
|
||||
|
||||
Robin(Archive parentArc, int rows, boolean shouldInitialize) throws IOException {
|
||||
this.parentArc = parentArc;
|
||||
this.pointer = new RrdInt(this);
|
||||
this.values = new RrdDoubleArray(this, rows);
|
||||
this.rows = rows;
|
||||
if (shouldInitialize) {
|
||||
pointer.set(0);
|
||||
values.set(0, Double.NaN, rows);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all archived values.
|
||||
*
|
||||
* @return Array of double archive values, starting from the oldest one.
|
||||
* @throws IOException Thrown in case of I/O specific error.
|
||||
*/
|
||||
public double[] getValues() throws IOException {
|
||||
return getValues(0, rows);
|
||||
}
|
||||
|
||||
// stores single value
|
||||
void store(double newValue) throws IOException {
|
||||
int position = pointer.get();
|
||||
values.set(position, newValue);
|
||||
pointer.set((position + 1) % rows);
|
||||
}
|
||||
|
||||
// stores the same value several times
|
||||
void bulkStore(double newValue, int bulkCount) throws IOException {
|
||||
assert bulkCount <= rows: "Invalid number of bulk updates: " + bulkCount +
|
||||
" rows=" + rows;
|
||||
int position = pointer.get();
|
||||
// update tail
|
||||
int tailUpdateCount = Math.min(rows - position, bulkCount);
|
||||
values.set(position, newValue, tailUpdateCount);
|
||||
pointer.set((position + tailUpdateCount) % rows);
|
||||
// do we need to update from the start?
|
||||
int headUpdateCount = bulkCount - tailUpdateCount;
|
||||
if (headUpdateCount > 0) {
|
||||
values.set(0, newValue, headUpdateCount);
|
||||
pointer.set(headUpdateCount);
|
||||
}
|
||||
}
|
||||
|
||||
void update(double[] newValues) throws IOException {
|
||||
assert rows == newValues.length: "Invalid number of robin values supplied (" + newValues.length +
|
||||
"), exactly " + rows + " needed";
|
||||
pointer.set(0);
|
||||
values.writeDouble(0, newValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates archived values in bulk.
|
||||
*
|
||||
* @param newValues Array of double values to be stored in the archive
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if the length of the input array is different from the length of
|
||||
* this archive
|
||||
*/
|
||||
public void setValues(double[] newValues) throws IOException, RrdException {
|
||||
if (rows != newValues.length) {
|
||||
throw new RrdException("Invalid number of robin values supplied (" + newValues.length +
|
||||
"), exactly " + rows + " needed");
|
||||
}
|
||||
update(newValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)sets all values in this archive to the same value.
|
||||
*
|
||||
* @param newValue New value
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public void setValues(double newValue) throws IOException {
|
||||
double[] values = new double[rows];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = newValue;
|
||||
}
|
||||
update(values);
|
||||
}
|
||||
|
||||
String dump() throws IOException {
|
||||
StringBuffer buffer = new StringBuffer("Robin " + pointer.get() + "/" + rows + ": ");
|
||||
double[] values = getValues();
|
||||
for (double value : values) {
|
||||
buffer.append(Util.formatDouble(value, true)).append(" ");
|
||||
}
|
||||
buffer.append("\n");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the i-th value from the Robin archive.
|
||||
*
|
||||
* @param index Value index
|
||||
* @return Value stored in the i-th position (the oldest value has zero index)
|
||||
* @throws IOException Thrown in case of I/O specific error.
|
||||
*/
|
||||
public double getValue(int index) throws IOException {
|
||||
int arrayIndex = (pointer.get() + index) % rows;
|
||||
return values.get(arrayIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the i-th value in the Robin archive.
|
||||
*
|
||||
* @param index index in the archive (the oldest value has zero index)
|
||||
* @param value value to be stored
|
||||
* @throws IOException Thrown in case of I/O specific error.
|
||||
*/
|
||||
public void setValue(int index, double value) throws IOException {
|
||||
int arrayIndex = (pointer.get() + index) % rows;
|
||||
values.set(arrayIndex, value);
|
||||
}
|
||||
|
||||
double[] getValues(int index, int count) throws IOException {
|
||||
assert count <= rows: "Too many values requested: " + count + " rows=" + rows;
|
||||
int startIndex = (pointer.get() + index) % rows;
|
||||
int tailReadCount = Math.min(rows - startIndex, count);
|
||||
double[] tailValues = values.get(startIndex, tailReadCount);
|
||||
if (tailReadCount < count) {
|
||||
int headReadCount = count - tailReadCount;
|
||||
double[] headValues = values.get(0, headReadCount);
|
||||
double[] values = new double[count];
|
||||
int k = 0;
|
||||
for (double tailValue : tailValues) {
|
||||
values[k++] = tailValue;
|
||||
}
|
||||
for (double headValue : headValues) {
|
||||
values[k++] = headValue;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
else {
|
||||
return tailValues;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Archive object to which this Robin object belongs.
|
||||
*
|
||||
* @return Parent Archive object
|
||||
*/
|
||||
public Archive getParent() {
|
||||
return parentArc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the underlying array of archived values.
|
||||
*
|
||||
* @return Number of stored values
|
||||
*/
|
||||
public int getSize() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies object's internal state to another Robin object.
|
||||
*
|
||||
* @param other New Robin object to copy state to
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown if supplied argument is not a Robin object
|
||||
*/
|
||||
public void copyStateTo(RrdUpdater other) throws IOException, RrdException {
|
||||
if (!(other instanceof Robin)) {
|
||||
throw new RrdException(
|
||||
"Cannot copy Robin object to " + other.getClass().getName());
|
||||
}
|
||||
Robin robin = (Robin) other;
|
||||
int rowsDiff = rows - robin.rows;
|
||||
if (rowsDiff == 0) {
|
||||
// Identical dimensions. Do copy in BULK to speed things up
|
||||
robin.pointer.set(pointer.get());
|
||||
robin.values.writeBytes(values.readBytes());
|
||||
}
|
||||
else {
|
||||
// different sizes
|
||||
for (int i = 0; i < robin.rows; i++) {
|
||||
int j = i + rowsDiff;
|
||||
robin.store(j >= 0 ? getValue(j) : Double.NaN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters values stored in this archive based on the given boundary.
|
||||
* Archived values found to be outside of <code>[minValue, maxValue]</code> interval (inclusive)
|
||||
* will be silently replaced with <code>NaN</code>.
|
||||
*
|
||||
* @param minValue lower boundary
|
||||
* @param maxValue upper boundary
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public void filterValues(double minValue, double maxValue) throws IOException {
|
||||
for (int i = 0; i < rows; i++) {
|
||||
double value = values.get(i);
|
||||
if (!Double.isNaN(minValue) && !Double.isNaN(value) && minValue > value) {
|
||||
values.set(i, Double.NaN);
|
||||
}
|
||||
if (!Double.isNaN(maxValue) && !Double.isNaN(value) && maxValue < value) {
|
||||
values.set(i, Double.NaN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying storage (backend) object which actually performs all
|
||||
* I/O operations.
|
||||
*
|
||||
* @return I/O backend object
|
||||
*/
|
||||
public RrdBackend getRrdBackend() {
|
||||
return parentArc.getRrdBackend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to implement RrdUpdater interface. You should never call this method directly.
|
||||
*
|
||||
* @return Allocator object
|
||||
*/
|
||||
public RrdAllocator getRrdAllocator() {
|
||||
return parentArc.getRrdAllocator();
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class RrdAllocator {
|
||||
private long allocationPointer = 0L;
|
||||
|
||||
long allocate(long byteCount) throws IOException {
|
||||
long pointer = allocationPointer;
|
||||
allocationPointer += byteCount;
|
||||
return pointer;
|
||||
}
|
||||
}
|
||||
@@ -1,341 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base implementation class for all backend classes. Each Round Robin Database object
|
||||
* ({@link RrdDb} object) is backed with a single RrdBackend object which performs
|
||||
* actual I/O operations on the underlying storage. JRobin supports
|
||||
* three different bakcends out of the box:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>{@link RrdFileBackend}: objects of this class are created from the
|
||||
* {@link RrdFileBackendFactory} class. This was the default backend used in all
|
||||
* JRobin releases prior to 1.4.0. It uses java.io.* package and
|
||||
* RandomAccessFile class to store RRD data in files on the disk.
|
||||
* <p>
|
||||
* <li>{@link RrdNioBackend}: objects of this class are created from the
|
||||
* {@link RrdNioBackendFactory} class. The backend uses java.io.* and java.nio.*
|
||||
* classes (mapped ByteBuffer) to store RRD data in files on the disk. This backend is fast, very fast,
|
||||
* but consumes a lot of memory (borrowed not from the JVM but from the underlying operating system
|
||||
* directly). <b>This is the default backend used in JRobin since 1.4.0 release.</b>
|
||||
* <p>
|
||||
* <li>{@link RrdMemoryBackend}: objects of this class are created from the
|
||||
* {@link RrdMemoryBackendFactory} class. This backend stores all data in memory. Once
|
||||
* JVM exits, all data gets lost. The backend is extremely fast and memory hungry.
|
||||
* </ul>
|
||||
* <p>
|
||||
* To create your own backend in order to provide some custom type of RRD storage,
|
||||
* you should do the following:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>Create your custom RrdBackend class (RrdCustomBackend, for example)
|
||||
* by extending RrdBackend class. You have to implement all abstract methods defined
|
||||
* in the base class.
|
||||
* <p>
|
||||
* <li>Create your custom RrdBackendFactory class (RrdCustomBackendFactory,
|
||||
* for example) by extending RrdBackendFactory class. You have to implement all
|
||||
* abstract methods defined in the base class. Your custom factory class will actually
|
||||
* create custom backend objects when necessary.
|
||||
* <p>
|
||||
* <li>Create instance of your custom RrdBackendFactory and register it as a regular
|
||||
* factory available to JRobin framework. See javadoc for {@link RrdBackendFactory} to
|
||||
* find out how to do this
|
||||
* </ul>
|
||||
*/
|
||||
public abstract class RrdBackend {
|
||||
private static boolean s_instanceCreated = false;
|
||||
private String m_path = null;
|
||||
private boolean m_readOnly = false;
|
||||
|
||||
/**
|
||||
* Creates backend for a RRD storage with the given path.
|
||||
*
|
||||
* @param path String identifying RRD storage. For files on the disk, this
|
||||
* argument should represent file path. Other storage types might interpret
|
||||
* this argument differently.
|
||||
*/
|
||||
protected RrdBackend(final String path) {
|
||||
this(path, false);
|
||||
}
|
||||
|
||||
protected RrdBackend(final String path, final boolean readOnly) {
|
||||
m_path = path;
|
||||
m_readOnly = readOnly;
|
||||
RrdBackend.setInstanceCreated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to the storage.
|
||||
*
|
||||
* @return Storage path
|
||||
*/
|
||||
public String getPath() {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the RRD ReadOnly?
|
||||
*
|
||||
* @return True if the RRD is read only, false if not.
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return m_readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an array of bytes to the underlying storage starting from the given
|
||||
* storage offset.
|
||||
*
|
||||
* @param offset Storage offset.
|
||||
* @param b Array of bytes that should be copied to the underlying storage
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
protected abstract void write(long offset, byte[] b) throws IOException;
|
||||
|
||||
/**
|
||||
* Reads an array of bytes from the underlying storage starting from the given
|
||||
* storage offset.
|
||||
*
|
||||
* @param offset Storage offset.
|
||||
* @param b Array which receives bytes from the underlying storage
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
protected abstract void read(long offset, byte[] b) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the number of RRD bytes in the underlying storage.
|
||||
*
|
||||
* @return Number of RRD bytes in the storage.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public abstract long getLength() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the number of bytes in the underlying RRD storage.
|
||||
* This method is called only once, immediately after a new RRD storage gets created.
|
||||
*
|
||||
* @param length Length of the underlying RRD storage in bytes.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected abstract void setLength(long length) throws IOException;
|
||||
|
||||
/**
|
||||
* Closes the underlying backend.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method suggests the caching policy to the JRobin frontend (high-level) classes. If <code>true</code>
|
||||
* is returned, frontent classes will cache frequently used parts of a RRD file in memory to improve
|
||||
* performance. If <code>false</code> is returned, high level classes will never cache RRD file sections
|
||||
* in memory.
|
||||
*
|
||||
* @return <code>true</code> if file caching is enabled, <code>false</code> otherwise. By default, the
|
||||
* method returns <code>true</code> but it can be overriden in subclasses.
|
||||
*/
|
||||
protected boolean isCachingAllowed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all RRD bytes from the underlying storage
|
||||
*
|
||||
* @return RRD bytes
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public final byte[] readAll() throws IOException {
|
||||
final byte[] b = new byte[(int) getLength()];
|
||||
read(0, b);
|
||||
return b;
|
||||
}
|
||||
|
||||
final void writeInt(final long offset, final int value) throws IOException {
|
||||
write(offset, getIntBytes(value));
|
||||
}
|
||||
|
||||
final void writeLong(final long offset, final long value) throws IOException {
|
||||
write(offset, getLongBytes(value));
|
||||
}
|
||||
|
||||
final void writeDouble(final long offset, final double value) throws IOException {
|
||||
write(offset, getDoubleBytes(value));
|
||||
}
|
||||
|
||||
final void writeDouble(final long offset, final double value, final int count) throws IOException {
|
||||
final byte[] b = getDoubleBytes(value);
|
||||
final byte[] image = new byte[8 * count];
|
||||
for (int i = 0, k = 0; i < count; i++) {
|
||||
image[k++] = b[0];
|
||||
image[k++] = b[1];
|
||||
image[k++] = b[2];
|
||||
image[k++] = b[3];
|
||||
image[k++] = b[4];
|
||||
image[k++] = b[5];
|
||||
image[k++] = b[6];
|
||||
image[k++] = b[7];
|
||||
}
|
||||
write(offset, image);
|
||||
}
|
||||
|
||||
final void writeDouble(final long offset, final double[] values) throws IOException {
|
||||
final int count = values.length;
|
||||
final byte[] image = new byte[8 * count];
|
||||
for (int i = 0, k = 0; i < count; i++) {
|
||||
final byte[] b = getDoubleBytes(values[i]);
|
||||
image[k++] = b[0];
|
||||
image[k++] = b[1];
|
||||
image[k++] = b[2];
|
||||
image[k++] = b[3];
|
||||
image[k++] = b[4];
|
||||
image[k++] = b[5];
|
||||
image[k++] = b[6];
|
||||
image[k++] = b[7];
|
||||
}
|
||||
write(offset, image);
|
||||
}
|
||||
|
||||
final void writeString(final long offset, final String rawValue) throws IOException {
|
||||
final String value = rawValue.trim();
|
||||
final byte[] b = new byte[RrdPrimitive.STRING_LENGTH * 2];
|
||||
for (int i = 0, k = 0; i < RrdPrimitive.STRING_LENGTH; i++) {
|
||||
final char c = (i < value.length()) ? value.charAt(i) : ' ';
|
||||
final byte[] cb = getCharBytes(c);
|
||||
b[k++] = cb[0];
|
||||
b[k++] = cb[1];
|
||||
}
|
||||
write(offset, b);
|
||||
}
|
||||
|
||||
final int readInt(final long offset) throws IOException {
|
||||
final byte[] b = new byte[4];
|
||||
read(offset, b);
|
||||
return getInt(b);
|
||||
}
|
||||
|
||||
final long readLong(final long offset) throws IOException {
|
||||
final byte[] b = new byte[8];
|
||||
read(offset, b);
|
||||
return getLong(b);
|
||||
}
|
||||
|
||||
final double readDouble(final long offset) throws IOException {
|
||||
final byte[] b = new byte[8];
|
||||
read(offset, b);
|
||||
return getDouble(b);
|
||||
}
|
||||
|
||||
final double[] readDouble(final long offset, final int count) throws IOException {
|
||||
final int byteCount = 8 * count;
|
||||
final byte[] image = new byte[byteCount];
|
||||
read(offset, image);
|
||||
final double[] values = new double[count];
|
||||
for (int i = 0, k = -1; i < count; i++) {
|
||||
final byte[] b = new byte[] {
|
||||
image[++k], image[++k], image[++k], image[++k],
|
||||
image[++k], image[++k], image[++k], image[++k]
|
||||
};
|
||||
values[i] = getDouble(b);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
final String readString(final long offset) throws IOException {
|
||||
final byte[] b = new byte[RrdPrimitive.STRING_LENGTH * 2];
|
||||
final char[] c = new char[RrdPrimitive.STRING_LENGTH];
|
||||
read(offset, b);
|
||||
for (int i = 0, k = -1; i < RrdPrimitive.STRING_LENGTH; i++) {
|
||||
final byte[] cb = new byte[] {b[++k], b[++k]};
|
||||
c[i] = getChar(cb);
|
||||
}
|
||||
return new String(c).trim();
|
||||
}
|
||||
|
||||
// static helper methods
|
||||
|
||||
private static byte[] getIntBytes(final int value) {
|
||||
final byte[] b = new byte[4];
|
||||
b[0] = (byte) ((value >>> 24) & 0xFF);
|
||||
b[1] = (byte) ((value >>> 16) & 0xFF);
|
||||
b[2] = (byte) ((value >>> 8) & 0xFF);
|
||||
b[3] = (byte) ((value) & 0xFF);
|
||||
return b;
|
||||
}
|
||||
|
||||
private static byte[] getLongBytes(final long value) {
|
||||
final byte[] b = new byte[8];
|
||||
b[0] = (byte) ((int) (value >>> 56) & 0xFF);
|
||||
b[1] = (byte) ((int) (value >>> 48) & 0xFF);
|
||||
b[2] = (byte) ((int) (value >>> 40) & 0xFF);
|
||||
b[3] = (byte) ((int) (value >>> 32) & 0xFF);
|
||||
b[4] = (byte) ((int) (value >>> 24) & 0xFF);
|
||||
b[5] = (byte) ((int) (value >>> 16) & 0xFF);
|
||||
b[6] = (byte) ((int) (value >>> 8) & 0xFF);
|
||||
b[7] = (byte) ((int) (value) & 0xFF);
|
||||
return b;
|
||||
}
|
||||
|
||||
private static byte[] getCharBytes(final char value) {
|
||||
final byte[] b = new byte[2];
|
||||
b[0] = (byte) ((value >>> 8) & 0xFF);
|
||||
b[1] = (byte) ((value) & 0xFF);
|
||||
return b;
|
||||
}
|
||||
|
||||
private static byte[] getDoubleBytes(final double value) {
|
||||
return getLongBytes(Double.doubleToLongBits(value));
|
||||
}
|
||||
|
||||
private static int getInt(final byte[] b) {
|
||||
assert b.length == 4: "Invalid number of bytes for integer conversion";
|
||||
return ((b[0] << 24) & 0xFF000000) + ((b[1] << 16) & 0x00FF0000) +
|
||||
((b[2] << 8) & 0x0000FF00) + (b[3] & 0x000000FF);
|
||||
}
|
||||
|
||||
private static long getLong(final byte[] b) {
|
||||
assert b.length == 8: "Invalid number of bytes for long conversion";
|
||||
int high = getInt(new byte[] {b[0], b[1], b[2], b[3]});
|
||||
int low = getInt(new byte[] {b[4], b[5], b[6], b[7]});
|
||||
return ((long) (high) << 32) + (low & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
private static char getChar(final byte[] b) {
|
||||
assert b.length == 2: "Invalid number of bytes for char conversion";
|
||||
return (char) (((b[0] << 8) & 0x0000FF00)
|
||||
+ (b[1] & 0x000000FF));
|
||||
}
|
||||
|
||||
private static double getDouble(final byte[] b) {
|
||||
assert b.length == 8: "Invalid number of bytes for double conversion";
|
||||
return Double.longBitsToDouble(getLong(b));
|
||||
}
|
||||
|
||||
private static void setInstanceCreated() {
|
||||
s_instanceCreated = true;
|
||||
}
|
||||
|
||||
static boolean isInstanceCreated() {
|
||||
return s_instanceCreated;
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Base (abstract) backend factory class which holds references to all concrete
|
||||
* backend factories and defines abstract methods which must be implemented in
|
||||
* all concrete factory implementations.
|
||||
* <p>
|
||||
* Factory classes are used to create concrete {@link RrdBackend} implementations.
|
||||
* Each factory creates unlimited number of specific backend objects.
|
||||
* <p>
|
||||
* JRobin supports four different backend types (backend factories) out of the box:<p>
|
||||
* <ul>
|
||||
* <li>{@link RrdFileBackend}: objects of this class are created from the
|
||||
* {@link RrdFileBackendFactory} class. This was the default backend used in all
|
||||
* JRobin releases before 1.4.0 release. It uses java.io.* package and RandomAccessFile class to store
|
||||
* RRD data in files on the disk.
|
||||
* <p>
|
||||
* <li>{@link RrdSafeFileBackend}: objects of this class are created from the
|
||||
* {@link RrdSafeFileBackendFactory} class. It uses java.io.* package and RandomAccessFile class to store
|
||||
* RRD data in files on the disk. This backend is SAFE:
|
||||
* it locks the underlying RRD file during update/fetch operations, and caches only static
|
||||
* parts of a RRD file in memory. Therefore, this backend is safe to be used when RRD files should
|
||||
* be shared <b>between several JVMs</b> at the same time. However, this backend is *slow* since it does
|
||||
* not use fast java.nio.* package (it's still based on the RandomAccessFile class).
|
||||
* <p>
|
||||
* <li>{@link RrdNioBackend}: objects of this class are created from the
|
||||
* {@link RrdNioBackendFactory} class. The backend uses java.io.* and java.nio.*
|
||||
* classes (mapped ByteBuffer) to store RRD data in files on the disk. This is the default backend
|
||||
* since 1.4.0 release.
|
||||
* <p>
|
||||
* <li>{@link RrdMemoryBackend}: objects of this class are created from the
|
||||
* {@link RrdMemoryBackendFactory} class. This backend stores all data in memory. Once
|
||||
* JVM exits, all data gets lost. The backend is extremely fast and memory hungry.
|
||||
* </ul>
|
||||
* <p>
|
||||
* Each backend factory is identifed by its {@link #getFactoryName() name}. Constructors
|
||||
* are provided in the {@link RrdDb} class to create RrdDb objects (RRD databases)
|
||||
* backed with a specific backend.
|
||||
* <p>
|
||||
* See javadoc for {@link RrdBackend} to find out how to create your custom backends.
|
||||
*/
|
||||
public abstract class RrdBackendFactory {
|
||||
private static final HashMap<String, RrdBackendFactory> factories = new HashMap<String, RrdBackendFactory>();
|
||||
private static RrdBackendFactory defaultFactory;
|
||||
|
||||
static {
|
||||
try {
|
||||
RrdFileBackendFactory fileFactory = new RrdFileBackendFactory();
|
||||
registerFactory(fileFactory);
|
||||
RrdJRobin14FileBackendFactory jrobin14Factory = new RrdJRobin14FileBackendFactory();
|
||||
registerFactory(jrobin14Factory);
|
||||
RrdMemoryBackendFactory memoryFactory = new RrdMemoryBackendFactory();
|
||||
registerFactory(memoryFactory);
|
||||
RrdNioBackendFactory nioFactory = new RrdNioBackendFactory();
|
||||
registerFactory(nioFactory);
|
||||
RrdSafeFileBackendFactory safeFactory = new RrdSafeFileBackendFactory();
|
||||
registerFactory(safeFactory);
|
||||
RrdNioByteBufferBackendFactory nioByteBufferFactory = new RrdNioByteBufferBackendFactory();
|
||||
registerFactory(nioByteBufferFactory);
|
||||
selectDefaultFactory();
|
||||
}
|
||||
catch (RrdException e) {
|
||||
throw new RuntimeException("FATAL: Cannot register RRD backend factories: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void selectDefaultFactory() throws RrdException {
|
||||
setDefaultFactory("FILE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns backend factory for the given backend factory name.
|
||||
*
|
||||
* @param name Backend factory name. Initially supported names are:<p>
|
||||
* <ul>
|
||||
* <li><b>FILE</b>: Default factory which creates backends based on the
|
||||
* java.io.* package. RRD data is stored in files on the disk
|
||||
* <li><b>SAFE</b>: Default factory which creates backends based on the
|
||||
* java.io.* package. RRD data is stored in files on the disk. This backend
|
||||
* is "safe". Being safe means that RRD files can be safely shared between
|
||||
* several JVM's.
|
||||
* <li><b>NIO</b>: Factory which creates backends based on the
|
||||
* java.nio.* package. RRD data is stored in files on the disk
|
||||
* <li><b>MEMORY</b>: Factory which creates memory-oriented backends.
|
||||
* RRD data is stored in memory, it gets lost as soon as JVM exits.
|
||||
* </ul>
|
||||
* @return Backend factory for the given factory name
|
||||
* @throws RrdException Thrown if no factory with the given name
|
||||
* is available.
|
||||
*/
|
||||
public static synchronized RrdBackendFactory getFactory(final String name) throws RrdException {
|
||||
final RrdBackendFactory factory = factories.get(name);
|
||||
if (factory != null) {
|
||||
return factory;
|
||||
}
|
||||
throw new RrdException("No backend factory found with the name specified [" + name + "]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new (custom) backend factory within the JRobin framework.
|
||||
*
|
||||
* @param factory Factory to be registered
|
||||
* @throws RrdException Thrown if the name of the specified factory is already
|
||||
* used.
|
||||
*/
|
||||
public static synchronized void registerFactory(final RrdBackendFactory factory) throws RrdException {
|
||||
final String name = factory.getFactoryName();
|
||||
if (!factories.containsKey(name)) {
|
||||
factories.put(name, factory);
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Backend factory of this name2 (" + name + ") already exists and cannot be registered");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new (custom) backend factory within the JRobin framework and sets this
|
||||
* factory as the default.
|
||||
*
|
||||
* @param factory Factory to be registered and set as default
|
||||
* @throws RrdException Thrown if the name of the specified factory is already
|
||||
* used.
|
||||
*/
|
||||
public static synchronized void registerAndSetAsDefaultFactory(final RrdBackendFactory factory) throws RrdException {
|
||||
registerFactory(factory);
|
||||
setDefaultFactory(factory.getFactoryName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the defaul backend factory. This factory is used to construct
|
||||
* {@link RrdDb} objects if no factory is specified in the RrdDb constructor.
|
||||
*
|
||||
* @return Default backend factory.
|
||||
*/
|
||||
public static RrdBackendFactory getDefaultFactory() {
|
||||
return defaultFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the default backend factory with a new one. This method must be called before
|
||||
* the first RRD gets created. <p>
|
||||
*
|
||||
* @param factoryName Name of the default factory. Out of the box, JRobin supports four
|
||||
* different RRD backends: "FILE" (java.io.* based), "SAFE" (java.io.* based - use this
|
||||
* backend if RRD files may be accessed from several JVMs at the same time),
|
||||
* "NIO" (java.nio.* based) and "MEMORY" (byte[] based).
|
||||
* @throws RrdException Thrown if invalid factory name is supplied or not called before
|
||||
* the first RRD is created.
|
||||
*/
|
||||
public static void setDefaultFactory(final String factoryName) throws RrdException {
|
||||
// We will allow this only if no RRDs are created
|
||||
if (!RrdBackend.isInstanceCreated()) {
|
||||
defaultFactory = getFactory(factoryName);
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Could not change the default backend factory. This method must be called before the first RRD gets created");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the RRD backend has created an instance yet.
|
||||
*
|
||||
* @return True if the backend instance is created, false if not.
|
||||
*/
|
||||
public static boolean isInstanceCreated() {
|
||||
return RrdBackend.isInstanceCreated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates RrdBackend object for the given storage path.
|
||||
*
|
||||
* @param path Storage path
|
||||
* @param readOnly True, if the storage should be accessed in read/only mode.
|
||||
* False otherwise.
|
||||
* @return Backend object which handles all I/O operations for the given storage path
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected abstract RrdBackend open(String path, boolean readOnly) throws IOException;
|
||||
|
||||
/**
|
||||
* Method to determine if a storage with the given path already exists.
|
||||
*
|
||||
* @param path Storage path
|
||||
* @return True, if such storage exists, false otherwise.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected abstract boolean exists(String path) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the name (primary ID) for the factory.
|
||||
*
|
||||
* @return Name of the factory.
|
||||
*/
|
||||
public abstract String getFactoryName();
|
||||
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[name=" + getFactoryName() + "]";
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,932 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
/**
|
||||
* This class should be used to synchronize access to RRD files
|
||||
* in a multithreaded environment. This class should be also used to prevent openning of
|
||||
* too many RRD files at the same time (thus avoiding operating system limits)
|
||||
*/
|
||||
|
||||
public class RrdDbPool {
|
||||
/**
|
||||
* Initial capacity of the pool i.e. maximum number of simultaneously open RRD files. The pool will
|
||||
* never open too many RRD files at the same time.
|
||||
*/
|
||||
public static final int INITIAL_CAPACITY = 200;
|
||||
private static RrdDbPool instance;
|
||||
|
||||
private int capacity = INITIAL_CAPACITY;
|
||||
private HashMap<String, RrdEntry> rrdMap = new HashMap<String, RrdEntry>(INITIAL_CAPACITY);
|
||||
|
||||
/**
|
||||
* Creates a single instance of the class on the first call, or returns already existing one.
|
||||
*
|
||||
* @return Single instance of this class
|
||||
* @throws RrdException Thrown if the default RRD backend is not derived from the {@link RrdFileBackendFactory}
|
||||
*/
|
||||
public synchronized static RrdDbPool getInstance() throws RrdException {
|
||||
if (instance == null) {
|
||||
instance = new RrdDbPool();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private RrdDbPool() throws RrdException {
|
||||
RrdBackendFactory factory = RrdBackendFactory.getDefaultFactory();
|
||||
if (!(factory instanceof RrdFileBackendFactory)) {
|
||||
throw new RrdException("Cannot create instance of " + getClass().getName() + " with " +
|
||||
"a default backend factory not derived from RrdFileBackendFactory");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a RrdDb reference for the given RRD file path.<p>
|
||||
* <ul>
|
||||
* <li>If the file is already open, previously returned RrdDb reference will be returned. Its usage count
|
||||
* will be incremented by one.
|
||||
* <li>If the file is not already open and the number of already open RRD files is less than
|
||||
* {@link #INITIAL_CAPACITY}, the file will be open and a new RrdDb reference will be returned.
|
||||
* If the file is not already open and the number of already open RRD files is equal to
|
||||
* {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
|
||||
* </ul>
|
||||
*
|
||||
* @param path Path to existing RRD file
|
||||
* @return reference for the give RRD file
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException {
|
||||
String canonicalPath = Util.getCanonicalPath(path);
|
||||
while (!rrdMap.containsKey(canonicalPath) && rrdMap.size() >= capacity) {
|
||||
try {
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RrdException(e);
|
||||
}
|
||||
}
|
||||
if (rrdMap.containsKey(canonicalPath)) {
|
||||
// already open, just increase usage count
|
||||
RrdEntry entry = rrdMap.get(canonicalPath);
|
||||
entry.count++;
|
||||
return entry.rrdDb;
|
||||
}
|
||||
else {
|
||||
// not open, open it now and add to the map
|
||||
RrdDb rrdDb = new RrdDb(canonicalPath);
|
||||
rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
|
||||
return rrdDb;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a RrdDb reference for the given RRD file definition object.<p>
|
||||
* <ul>
|
||||
* <li>If the file with the path specified in the RrdDef object is already open,
|
||||
* the method blocks until the file is closed.
|
||||
* <li>If the file is not already open and the number of already open RRD files is less than
|
||||
* {@link #INITIAL_CAPACITY}, a new RRD file will be created and a its RrdDb reference will be returned.
|
||||
* If the file is not already open and the number of already open RRD files is equal to
|
||||
* {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
|
||||
* </ul>
|
||||
*
|
||||
* @param rrdDef Definition of the RRD file to be created
|
||||
* @return Reference to the newly created RRD file
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException {
|
||||
String canonicalPath = Util.getCanonicalPath(rrdDef.getPath());
|
||||
while (rrdMap.containsKey(canonicalPath) || rrdMap.size() >= capacity) {
|
||||
try {
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RrdException(e);
|
||||
}
|
||||
}
|
||||
RrdDb rrdDb = new RrdDb(rrdDef);
|
||||
rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
|
||||
return rrdDb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a RrdDb reference for the given path. The file will be created from
|
||||
* external data (from XML dump, RRD file or RRDTool's binary RRD file).<p>
|
||||
* <ul>
|
||||
* <li>If the file with the path specified is already open,
|
||||
* the method blocks until the file is closed.
|
||||
* <li>If the file is not already open and the number of already open RRD files is less than
|
||||
* {@link #INITIAL_CAPACITY}, a new RRD file will be created and a its RrdDb reference will be returned.
|
||||
* If the file is not already open and the number of already open RRD files is equal to
|
||||
* {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
|
||||
* </ul>
|
||||
*
|
||||
* @param path Path to RRD file which should be created
|
||||
* @param sourcePath Path to external data which is to be converted to JRobin's native RRD file format
|
||||
* @return Reference to the newly created RRD file
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public synchronized RrdDb requestRrdDb(String path, String sourcePath)
|
||||
throws IOException, RrdException,RrdException {
|
||||
String canonicalPath = Util.getCanonicalPath(path);
|
||||
while (rrdMap.containsKey(canonicalPath) || rrdMap.size() >= capacity) {
|
||||
try {
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RrdException(e);
|
||||
}
|
||||
}
|
||||
RrdDb rrdDb = new RrdDb(canonicalPath, sourcePath);
|
||||
rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
|
||||
return rrdDb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases RrdDb reference previously obtained from the pool. When a reference is released, its usage
|
||||
* count is decremented by one. If usage count drops to zero, the underlying RRD file will be closed.
|
||||
*
|
||||
* @param rrdDb RrdDb reference to be returned to the pool
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public synchronized void release(RrdDb rrdDb) throws IOException, RrdException {
|
||||
// null pointer should not kill the thread, just ignore it
|
||||
if (rrdDb == null) {
|
||||
return;
|
||||
}
|
||||
String canonicalPath = Util.getCanonicalPath(rrdDb.getPath());
|
||||
if (!rrdMap.containsKey(canonicalPath)) {
|
||||
throw new RrdException("Could not release [" + canonicalPath + "], the file was never requested");
|
||||
}
|
||||
RrdEntry entry = rrdMap.get(canonicalPath);
|
||||
if (--entry.count <= 0) {
|
||||
// no longer used
|
||||
rrdMap.remove(canonicalPath);
|
||||
notifyAll();
|
||||
entry.rrdDb.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of simultaneously open RRD files.
|
||||
*
|
||||
* @return maximum number of simultaneously open RRD files
|
||||
*/
|
||||
public synchronized int getCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of simultaneously open RRD files.
|
||||
*
|
||||
* @param capacity Maximum number of simultaneously open RRD files.
|
||||
*/
|
||||
public synchronized void setCapacity(int capacity) {
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of open file names.
|
||||
*
|
||||
* @return Array with canonical paths to open RRD files held in the pool.
|
||||
*/
|
||||
public synchronized String[] getOpenFiles() {
|
||||
return rrdMap.keySet().toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of open RRD files.
|
||||
*
|
||||
* @return Number of currently open RRD files held in the pool.
|
||||
*/
|
||||
public synchronized int getOpenFileCount() {
|
||||
return rrdMap.size();
|
||||
}
|
||||
|
||||
private final static class RrdEntry {
|
||||
RrdDb rrdDb;
|
||||
int count;
|
||||
|
||||
RrdEntry(final RrdDb rrdDb) {
|
||||
this.rrdDb = rrdDb;
|
||||
this.count = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OLDER VERSION IS HERE
|
||||
|
||||
///* ============================================================
|
||||
// * JRobin : Pure java implementation of RRDTool's functionality
|
||||
// * ============================================================
|
||||
// *
|
||||
// * Project Info: http://www.jrobin.org
|
||||
// * Project Lead: Sasa Markovic (saxon@jrobin.org);
|
||||
// *
|
||||
// * (C) Copyright 2003-2005, by Sasa Markovic.
|
||||
// *
|
||||
// * Developers: Sasa Markovic (saxon@jrobin.org)
|
||||
// *
|
||||
// *
|
||||
// * This library is free software; you can redistribute it and/or modify it under the terms
|
||||
// * of the GNU Lesser General Public License as published by the Free Software Foundation;
|
||||
// * either version 2.1 of the License, or (at your option) any later version.
|
||||
// *
|
||||
// * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
// * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// * See the GNU Lesser General Public License for more details.
|
||||
// *
|
||||
// * You should have received a copy of the GNU Lesser General Public License along with this
|
||||
// * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
// * Boston, MA 02111-1307, USA.
|
||||
// */
|
||||
//package org.jrobin.core;
|
||||
//
|
||||
//import java.io.IOException;
|
||||
//import java.util.HashMap;
|
||||
//import java.util.Iterator;
|
||||
//import java.util.LinkedHashMap;
|
||||
//import java.util.Set;
|
||||
//
|
||||
///**
|
||||
// * Class to represent the pool of open RRD files.<p>
|
||||
// *
|
||||
// * To open already existing RRD file with JRobin, you have to create a
|
||||
// * {@link org.jrobin.core.RrdDb RrdDb} object by specifying RRD file path
|
||||
// * as constructor argument. This operation can be time consuming
|
||||
// * especially with large RRD files with many datasources and
|
||||
// * several long archives.<p>
|
||||
// *
|
||||
// * In a multithreaded environment you might probably need a reference to the
|
||||
// * same RRD file from two different threads (RRD file updates are performed in
|
||||
// * one thread but data fetching and graphing is performed in another one). To make
|
||||
// * the RrdDb construction process more efficient it might be convenient to open all
|
||||
// * RRD files in a centralized place. That's the purpose of RrdDbPool class.<p>
|
||||
// *
|
||||
// * How does it work? The typical usage scenario goes like this:<p>
|
||||
// *
|
||||
// * <pre>
|
||||
// * // obtain instance to RrdDbPool object
|
||||
// * RrdDbPool pool = RrdDbPool.getInstance();
|
||||
// *
|
||||
// * // request a reference to RrdDb object
|
||||
// * String path = "some_relative_or_absolute_path_to_any_RRD_file";
|
||||
// * RrdDb rrdDb = RrdDbPool.requestRrdDb(path);
|
||||
// *
|
||||
// * // reference obtained, do whatever you want with it...
|
||||
// * ...
|
||||
// * ...
|
||||
// *
|
||||
// * // once you don't need the reference, release it.
|
||||
// * // DO NOT CALL rrdDb.close() - files no longer in use are eventually closed by the pool
|
||||
// * pool.release(rrdDb);
|
||||
// * </pre>
|
||||
// *
|
||||
// * It's that simple. When the reference is requested for the first time, RrdDbPool will open the RRD file
|
||||
// * for you and make some internal note that the RRD file is used only once. When the reference
|
||||
// * to the same file (same RRD file path) is requested for the second time, the same RrdDb
|
||||
// * reference will be returned, and its usage count will be increased by one. When the
|
||||
// * reference is released its usage count will be decremented by one.<p>
|
||||
// *
|
||||
// * When the reference count drops to zero, RrdDbPool will not close the underlying
|
||||
// * RRD file immediatelly. Instead of it, it will be marked as 'eligible for closing'.
|
||||
// * If someone request the same RRD file again (before it gets closed), the same
|
||||
// * reference will be returned again.<p>
|
||||
// *
|
||||
// * RrdDbPool has a 'garbage collector' which runs in a separate, low-priority
|
||||
// * thread and gets activated only when the number of RRD files kept in the
|
||||
// * pool is too big (greater than number returned from {@link #getCapacity getCapacity()}).
|
||||
// * Only RRD files with a reference count equal to zero
|
||||
// * will be eligible for closing. Unreleased RrdDb references are never invalidated.
|
||||
// * RrdDbPool object keeps track of the time when each RRD file
|
||||
// * becomes eligible for closing so that the oldest RRD file gets closed first.<p>
|
||||
// *
|
||||
// * Initial RrdDbPool capacity is set to {@link #INITIAL_CAPACITY}. Use {@link #setCapacity(int)}
|
||||
// * method to change it at any time.<p>
|
||||
// *
|
||||
// * <b>WARNING:</b>Never use close() method on the reference returned from the pool.
|
||||
// * When the reference is no longer needed, return it to the pool with the
|
||||
// * {@link #release(RrdDb) release()} method.<p>
|
||||
// *
|
||||
// * However, you are not forced to use RrdDbPool methods to obtain RrdDb references
|
||||
// * to RRD files, 'ordinary' RrdDb constructors are still available. But RrdDbPool class
|
||||
// * offers serious performance improvement especially in complex applications with many
|
||||
// * threads and many simultaneously open RRD files.<p>
|
||||
// *
|
||||
// * The pool is thread-safe. Not that the {@link RrdDb} objects returned from the pool are
|
||||
// * also thread-safe<p>
|
||||
// *
|
||||
// * You should know that each operating system has its own internal limit on the number
|
||||
// * of simultaneously open files. The capacity of your RrdDbPool should be
|
||||
// * reasonably smaller than the limit imposed by your operating system.<p>
|
||||
// *
|
||||
// * <b>WARNING:</b> The pool cannot be used to manipulate RrdDb objects
|
||||
// * with {@link RrdBackend backends} different from default.<p>
|
||||
// */
|
||||
//public class RrdDbPool implements Runnable {
|
||||
// static final String GC_THREAD_NAME = "RrdDbPool GC thread";
|
||||
// static final String CLOSING_THREAD_NAME = "RrdDbPool closing thread";
|
||||
// private static final boolean DEBUG = false;
|
||||
//
|
||||
// // singleton pattern
|
||||
// private static RrdDbPool ourInstance;
|
||||
// private boolean closingOnExit = true;
|
||||
//
|
||||
// private Thread shutdownHook = new Thread(CLOSING_THREAD_NAME) {
|
||||
// public void run() {
|
||||
// try {
|
||||
// close();
|
||||
// }
|
||||
// catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// /**
|
||||
// * Constant to represent the maximum number of internally open RRD files
|
||||
// * which still does not force garbage collector (the process which closes RRD files) to run.
|
||||
// */
|
||||
// public static final int INITIAL_CAPACITY = 500;
|
||||
// private int capacity = INITIAL_CAPACITY, maxUsedCapacity;
|
||||
// private boolean active = true;
|
||||
//
|
||||
// /**
|
||||
// * Constant to represent the internal behaviour of the pool.
|
||||
// * Defaults to <code>true</code> but can be changed at runtime. See
|
||||
// * {@link #setLimitedCapacity(boolean)} for more information.
|
||||
// */
|
||||
// public static final boolean LIMITED_CAPACITY = false;
|
||||
// private boolean limitedCapacity = LIMITED_CAPACITY;
|
||||
//
|
||||
// /**
|
||||
// * Constant to represent the priority of the background thread which closes excessive RRD files
|
||||
// * which are no longer in use.
|
||||
// */
|
||||
// public static final int GC_THREAD_PRIORITY = /** Thread.NORM_PRIORITY - */ 1;
|
||||
//
|
||||
// private HashMap<String, RrdEntry> rrdMap = new HashMap<String, RrdEntry>(INITIAL_CAPACITY);
|
||||
// private LinkedHashMap<String, RrdEntry> rrdIdleMap = new LinkedHashMap<String, RrdEntry>(INITIAL_CAPACITY);
|
||||
// private RrdBackendFactory factory;
|
||||
// private int poolHitsCount = 0, poolRequestsCount = 0;
|
||||
//
|
||||
// /**
|
||||
// * Returns an instance to RrdDbPool object. Only one such object may exist in each JVM.
|
||||
// *
|
||||
// * @return Instance to RrdDbPool object.
|
||||
// */
|
||||
// public synchronized static RrdDbPool getInstance() {
|
||||
// if (ourInstance == null) {
|
||||
// ourInstance = new RrdDbPool();
|
||||
// ourInstance.startGarbageCollector();
|
||||
// }
|
||||
// return ourInstance;
|
||||
// }
|
||||
//
|
||||
// private RrdDbPool() {
|
||||
// setClosingOnExit(closingOnExit);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Checks the exiting behaviour of RrdDbPool.
|
||||
// * @return <code>True</code>, if all RRD files are to be closed
|
||||
// * when application invokes <code>System.exit()</code>.
|
||||
// * <code>False</code> otherwise. The default behaviour is <code>true</code>
|
||||
// * (all RRD files will be closed on exit).
|
||||
// */
|
||||
// public synchronized boolean isClosingOnExit() {
|
||||
// return closingOnExit;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Sets the exiting behaviour of RrdDbPool.
|
||||
// * @param closingOnExit <code>True</code>, if all RRD files are to be closed
|
||||
// * when application invokes <code>System.exit()</code>.
|
||||
// * <code>False</code> otherwise. The default behaviour is <code>true</code>
|
||||
// * (all RRD files will be closed on exit).
|
||||
// */
|
||||
// public synchronized void setClosingOnExit(boolean closingOnExit) {
|
||||
// Runtime runtime = Runtime.getRuntime();
|
||||
// runtime.removeShutdownHook(shutdownHook);
|
||||
// if(closingOnExit) {
|
||||
// runtime.addShutdownHook(shutdownHook);
|
||||
// }
|
||||
// this.closingOnExit = closingOnExit;
|
||||
// }
|
||||
//
|
||||
// private void startGarbageCollector() {
|
||||
// Thread gcThread = new Thread(this, GC_THREAD_NAME);
|
||||
// gcThread.setPriority(GC_THREAD_PRIORITY);
|
||||
// gcThread.setDaemon(true);
|
||||
// gcThread.start();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns a reference to an existing RRD file with the specified path.
|
||||
// * If the file is already open in the pool, existing reference to it will be returned.
|
||||
// * Otherwise, the file is open and a newly created reference to it is returned.
|
||||
// *
|
||||
// * @param path Relative or absolute path to a RRD file.
|
||||
// * @return Reference to a RrdDb object (RRD file).
|
||||
// * @throws IOException Thrown in case of I/O error.
|
||||
// * @throws RrdException Thrown in case of JRobin specific error.
|
||||
// */
|
||||
// public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException {
|
||||
// proveActive();
|
||||
// poolRequestsCount++;
|
||||
// String canonicalPath = getCanonicalPath(path);
|
||||
// for(;;) {
|
||||
// RrdEntry rrdEntry = rrdMap.get(canonicalPath);
|
||||
// if (rrdEntry != null) {
|
||||
// // already open, use it!
|
||||
// reportUsage(canonicalPath, rrdEntry);
|
||||
// poolHitsCount++;
|
||||
//// debug("CACHED: " + rrdEntry.dump());
|
||||
// return rrdEntry.getRrdDb();
|
||||
// }
|
||||
// else if(!limitedCapacity || rrdMap.size() < capacity) {
|
||||
// // not found, open it
|
||||
// RrdDb rrdDb = createRrdDb(path, null);
|
||||
// rrdEntry = new RrdEntry(rrdDb);
|
||||
// addRrdEntry(canonicalPath, rrdEntry);
|
||||
//// debug("ADDED: " + rrdEntry.dump());
|
||||
// return rrdDb;
|
||||
// }
|
||||
// else {
|
||||
// // we have to wait
|
||||
// try {
|
||||
// wait();
|
||||
// }
|
||||
// catch (InterruptedException e) {
|
||||
// throw new RrdException("Request for file '" + path + "' was interrupted");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns a reference to a new RRD file. The new file will have the specified
|
||||
// * relative or absolute path, and its contents will be provided from the specified
|
||||
// * XML file (RRDTool comaptible).
|
||||
// *
|
||||
// * @param path Relative or absolute path to a new RRD file.
|
||||
// * @param xmlPath Relative or absolute path to an existing XML dump file (RRDTool comaptible)
|
||||
// * @return Reference to a RrdDb object (RRD file).
|
||||
// * @throws IOException Thrown in case of I/O error.
|
||||
// * @throws RrdException Thrown in case of JRobin specific error.
|
||||
// */
|
||||
// public synchronized RrdDb requestRrdDb(String path, String xmlPath)
|
||||
// throws IOException, RrdException {
|
||||
// return requestNewRrdDb(path, xmlPath);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns a reference to a new RRD file. The new file will be created based on the
|
||||
// * definition contained in a RrdDef object.
|
||||
// *
|
||||
// * @param rrdDef RRD definition object
|
||||
// * @return Reference to a RrdDb object (RRD file).
|
||||
// * @throws IOException Thrown in case of I/O error.
|
||||
// * @throws RrdException Thrown in case of JRobin specific error.
|
||||
// */
|
||||
// public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException {
|
||||
// return requestNewRrdDb(rrdDef.getPath(), rrdDef);
|
||||
// }
|
||||
//
|
||||
// private RrdDb requestNewRrdDb(String path, Object creationDef) throws IOException, RrdException {
|
||||
// proveActive();
|
||||
// poolRequestsCount++;
|
||||
// String canonicalPath = getCanonicalPath(path);
|
||||
// for(;;) {
|
||||
// RrdEntry rrdEntry = rrdMap.get(canonicalPath);
|
||||
// if(rrdEntry != null) {
|
||||
// // already open
|
||||
// removeIfIdle(canonicalPath, rrdEntry);
|
||||
// }
|
||||
// else if(!limitedCapacity || rrdMap.size() < capacity) {
|
||||
// RrdDb rrdDb = createRrdDb(path, creationDef);
|
||||
// RrdEntry newRrdEntry = new RrdEntry(rrdDb);
|
||||
// addRrdEntry(canonicalPath, newRrdEntry);
|
||||
//// debug("ADDED: " + newRrdEntry.dump());
|
||||
// return rrdDb;
|
||||
// }
|
||||
// else {
|
||||
// // we have to wait
|
||||
// try {
|
||||
// wait();
|
||||
// }
|
||||
// catch (InterruptedException e) {
|
||||
// throw new RrdException("Request for file '" + path + "' was interrupted");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private RrdDb createRrdDb(String path, Object creationDef) throws RrdException, IOException {
|
||||
// if(creationDef == null) {
|
||||
// // existing RRD
|
||||
// return new RrdDb(path, getFactory());
|
||||
// }
|
||||
// else if(creationDef instanceof String) {
|
||||
// // XML input
|
||||
// return new RrdDb(path, (String) creationDef, getFactory());
|
||||
// }
|
||||
// else if(creationDef instanceof RrdDef) {
|
||||
// // RrdDef
|
||||
// return new RrdDb((RrdDef) creationDef, getFactory());
|
||||
// }
|
||||
// else {
|
||||
// throw new RrdException("Unexpected input object type: " +
|
||||
// creationDef.getClass().getName());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void reportUsage(String canonicalPath, RrdEntry rrdEntry) {
|
||||
// if (rrdEntry.reportUsage() == 1) {
|
||||
// // must not be garbage collected
|
||||
// rrdIdleMap.remove(canonicalPath);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void reportRelease(String canonicalPath, RrdEntry rrdEntry) {
|
||||
// if (rrdEntry.reportRelease() == 0) {
|
||||
// // ready to be garbage collected
|
||||
// rrdIdleMap.put(canonicalPath, rrdEntry);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void addRrdEntry(String canonicalPath, RrdEntry newRrdEntry) {
|
||||
// rrdMap.put(canonicalPath, newRrdEntry);
|
||||
// maxUsedCapacity = Math.max(rrdMap.size(), maxUsedCapacity);
|
||||
// // notify waiting threads
|
||||
// notifyAll();
|
||||
// }
|
||||
//
|
||||
// private void removeIfIdle(String canonicalPath, RrdEntry rrdEntry)
|
||||
// throws RrdException, IOException {
|
||||
// // already open, check if active (not released)
|
||||
// if (rrdEntry.isInUse()) {
|
||||
// // not released, not allowed here
|
||||
// throw new RrdException("Cannot create new RrdDb file: " +
|
||||
// "File '" + canonicalPath + "' already in use");
|
||||
// } else {
|
||||
// // open but released... safe to close it
|
||||
//// debug("WILL BE RECREATED: " + rrdEntry.dump());
|
||||
// removeRrdEntry(canonicalPath, rrdEntry);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void removeRrdEntry(String canonicalPath, RrdEntry rrdEntry) throws IOException {
|
||||
// rrdEntry.closeRrdDb();
|
||||
// rrdMap.remove(canonicalPath);
|
||||
// rrdIdleMap.remove(canonicalPath);
|
||||
//// debug("REMOVED: " + rrdEntry.dump());
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Method used to report that the reference to a RRD file is no longer needed. File that
|
||||
// * is no longer needed (all references to it are released) is marked 'eligible for
|
||||
// * closing'. It will be eventually closed by the pool when the number of open RRD files
|
||||
// * becomes too big. Most recently released files will be closed last.
|
||||
// *
|
||||
// * @param rrdDb Reference to RRD file that is no longer needed.
|
||||
// * @throws IOException Thrown in case of I/O error.
|
||||
// * @throws RrdException Thrown in case of JRobin specific error.
|
||||
// */
|
||||
// public synchronized void release(RrdDb rrdDb) throws IOException, RrdException {
|
||||
// proveActive();
|
||||
// if (rrdDb == null) {
|
||||
// // we don't want NullPointerException
|
||||
// return;
|
||||
// }
|
||||
// if (rrdDb.isClosed()) {
|
||||
// throw new RrdException("File " + rrdDb.getPath() + " already closed");
|
||||
// }
|
||||
// String canonicalPath = getCanonicalPath(rrdDb.getPath());
|
||||
// if (rrdMap.containsKey(canonicalPath)) {
|
||||
// RrdEntry rrdEntry = rrdMap.get(canonicalPath);
|
||||
// reportRelease(canonicalPath, rrdEntry);
|
||||
//// debug("RELEASED: " + rrdEntry.dump());
|
||||
// } else {
|
||||
// throw new RrdException("RRD file " + rrdDb.getPath() + " not in the pool");
|
||||
// }
|
||||
// // notify waiting threads
|
||||
// notifyAll();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * This method runs garbage collector in a separate thread. If the number of
|
||||
// * open RRD files kept in the pool is too big (greater than number
|
||||
// * returned from {@link #getCapacity getCapacity()}), garbage collector will try
|
||||
// * to close and remove RRD files with a reference count equal to zero.
|
||||
// * Never call this method directly.
|
||||
// */
|
||||
// public void run() {
|
||||
//// debug("GC: started");
|
||||
// while (active) {
|
||||
// synchronized (this) {
|
||||
// if (rrdMap.size() >= capacity && rrdIdleMap.size() > 0) {
|
||||
// try {
|
||||
// String canonicalPath = rrdIdleMap.keySet().iterator().next();
|
||||
// RrdEntry rrdEntry = rrdIdleMap.get(canonicalPath);
|
||||
//// debug("GC: closing " + rrdEntry.dump());
|
||||
// removeRrdEntry(canonicalPath, rrdEntry);
|
||||
// } catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// notifyAll();
|
||||
// }
|
||||
// else {
|
||||
// try {
|
||||
//// debug("GC: waiting");
|
||||
// wait();
|
||||
//// debug("GC: running");
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected void finalize() throws IOException {
|
||||
// close();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Clears the internal state of the pool entirely. All open RRD files are closed.
|
||||
// *
|
||||
// * @throws IOException Thrown in case of I/O related error.
|
||||
// */
|
||||
// public synchronized void reset() throws IOException {
|
||||
// Iterator<RrdEntry> it = rrdMap.values().iterator();
|
||||
// while (it.hasNext()) {
|
||||
// RrdEntry rrdEntry = it.next();
|
||||
// rrdEntry.closeRrdDb();
|
||||
// }
|
||||
// rrdMap.clear();
|
||||
// rrdIdleMap.clear();
|
||||
//// debug("Pool cleared");
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Closes the pool and all RRD files currently held in the pool.
|
||||
// * No further operations on the pool are allowed.
|
||||
// * @throws IOException Thrown in case of I/O error.
|
||||
// */
|
||||
// public synchronized void close() throws IOException {
|
||||
// if(active) {
|
||||
// active = false;
|
||||
// reset();
|
||||
//// debug("The pool is closed.");
|
||||
// }
|
||||
// else {
|
||||
//// debug("The pool is already closed!");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static String getCanonicalPath(String path) throws IOException {
|
||||
// return Util.getCanonicalPath(path);
|
||||
// }
|
||||
//
|
||||
// static void debug(String msg) {
|
||||
// if (DEBUG) {
|
||||
// System.out.println("POOL: " + msg);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns the internal state of the pool. Useful for debugging purposes.
|
||||
// *
|
||||
// * @param dumpFiles <code>true</code>, if dumped information should contain paths to open files
|
||||
// * currently held in the pool, <code>false</code> otherwise
|
||||
// * @return Internal pool state (with an optional list of open RRD files and
|
||||
// * the current number of usages for each one).
|
||||
// * @throws IOException Thrown in case of I/O error.
|
||||
// */
|
||||
// public synchronized String dump(boolean dumpFiles) throws IOException {
|
||||
// StringBuffer buff = new StringBuffer();
|
||||
// buff.append("==== POOL DUMP ===========================\n");
|
||||
// buff.append("open=" + rrdMap.size() + ", idle=" + rrdIdleMap.size() + "\n");
|
||||
// buff.append("capacity=" + capacity + ", " + "maxUsedCapacity=" + maxUsedCapacity + "\n");
|
||||
// buff.append("hits=" + poolHitsCount + ", " + "requests=" + poolRequestsCount + "\n");
|
||||
// buff.append("efficiency=" + getPoolEfficiency() + "\n");
|
||||
// if(dumpFiles) {
|
||||
// buff.append("---- CACHED FILES ------------------------\n");
|
||||
// Iterator<RrdEntry> it = rrdMap.values().iterator();
|
||||
// while (it.hasNext()) {
|
||||
// RrdEntry rrdEntry = it.next();
|
||||
// buff.append(rrdEntry.dump() + "\n");
|
||||
// }
|
||||
// }
|
||||
// return buff.toString();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns the complete internal state of the pool. Useful for debugging purposes.
|
||||
// *
|
||||
// * @return Internal pool state (with a list of open RRD files and the current number of
|
||||
// * usages for each one).
|
||||
// * @throws IOException Thrown in case of I/O error.
|
||||
// */
|
||||
// public synchronized String dump() throws IOException {
|
||||
// return dump(true);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns paths to all open files currently held in the pool.
|
||||
// * @return An array containing open file paths.
|
||||
// */
|
||||
// public synchronized String[] getCachedFilePaths() {
|
||||
// Set<String> keySet = rrdMap.keySet();
|
||||
// int n = keySet.size(), i = 0;
|
||||
// String[] files = new String[n];
|
||||
// Iterator<String> it = keySet.iterator();
|
||||
// while(it.hasNext()) {
|
||||
// files[i++] = it.next();
|
||||
// }
|
||||
// return files;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns maximum number of internally open RRD files
|
||||
// * which still does not force garbage collector to run.
|
||||
// *
|
||||
// * @return Desired nuber of open files held in the pool.
|
||||
// */
|
||||
// public synchronized int getCapacity() {
|
||||
// return capacity;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Sets maximum number of internally open RRD files
|
||||
// * which still does not force garbage collector to run.
|
||||
// *
|
||||
// * @param capacity Desired number of open files to hold in the pool
|
||||
// */
|
||||
// public synchronized void setCapacity(int capacity) {
|
||||
// this.capacity = capacity;
|
||||
//// debug("Capacity set to: " + capacity);
|
||||
// }
|
||||
//
|
||||
// private RrdBackendFactory getFactory() throws RrdException {
|
||||
// if (factory == null) {
|
||||
// factory = RrdBackendFactory.getDefaultFactory();
|
||||
// if (!(factory instanceof RrdFileBackendFactory)) {
|
||||
// factory = null;
|
||||
// throw new RrdException(
|
||||
// "RrdDbPool cannot work with factories not derived from RrdFileBackendFactory");
|
||||
// }
|
||||
// }
|
||||
// return factory;
|
||||
// }
|
||||
//
|
||||
// private class RrdEntry {
|
||||
// private RrdDb rrdDb;
|
||||
// private int usageCount = 1;
|
||||
//
|
||||
// public RrdEntry(RrdDb rrdDb) {
|
||||
// this.rrdDb = rrdDb;
|
||||
// }
|
||||
//
|
||||
// RrdDb getRrdDb() {
|
||||
// return rrdDb;
|
||||
// }
|
||||
//
|
||||
// int reportUsage() {
|
||||
// assert usageCount >= 0: "Unexpected reportUsage count: " + usageCount;
|
||||
// return ++usageCount;
|
||||
// }
|
||||
//
|
||||
// int reportRelease() {
|
||||
// assert usageCount > 0: "Unexpected reportRelease count: " + usageCount;
|
||||
// return --usageCount;
|
||||
// }
|
||||
//
|
||||
// boolean isInUse() {
|
||||
// return usageCount > 0;
|
||||
// }
|
||||
//
|
||||
// void closeRrdDb() throws IOException {
|
||||
// rrdDb.close();
|
||||
// }
|
||||
//
|
||||
// String dump() throws IOException {
|
||||
// String canonicalPath = getCanonicalPath(rrdDb.getPath());
|
||||
// return canonicalPath + " [" + usageCount + "]";
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Calculates pool's efficency ratio. The ratio is obtained by dividing the number of
|
||||
// * RrdDb requests served from the internal pool of open RRD files
|
||||
// * with the number of total RrdDb requests.
|
||||
// *
|
||||
// * @return Pool's efficiency ratio as a double between 1 (best) and 0 (worst).
|
||||
// * If no RrdDb reference was ever requested, 1 would be returned.
|
||||
// */
|
||||
// public synchronized double getPoolEfficiency() {
|
||||
// if (poolRequestsCount == 0) {
|
||||
// return 1.0;
|
||||
// }
|
||||
// double ratio = (double) poolHitsCount / (double) poolRequestsCount;
|
||||
// // round to 3 decimal digits
|
||||
// return Math.round(ratio * 1000.0) / 1000.0;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns the number of RRD requests served from the internal pool of open RRD files
|
||||
// *
|
||||
// * @return The number of pool "hits".
|
||||
// */
|
||||
// public synchronized int getPoolHitsCount() {
|
||||
// return poolHitsCount;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns the total number of RRD requests successfully served by this pool.
|
||||
// *
|
||||
// * @return Total number of RRD requests
|
||||
// */
|
||||
// public synchronized int getPoolRequestsCount() {
|
||||
// return poolRequestsCount;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Returns the maximum number of open RRD files over the lifetime
|
||||
// * of the pool.
|
||||
// * @return maximum number of open RRD files.
|
||||
// */
|
||||
// public synchronized int getMaxUsedCapacity() {
|
||||
// return maxUsedCapacity;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Checks the internal behaviour of the pool. See {@link #setLimitedCapacity(boolean)} for
|
||||
// * more information.
|
||||
// *
|
||||
// * @return <code>true</code> if the pool is 'flexible' (by not imposing the strict
|
||||
// * limit on the number of simultaneously open files), <code>false</code> otherwise.
|
||||
// */
|
||||
// public synchronized boolean isLimitedCapacity() {
|
||||
// return limitedCapacity;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Sets the behaviour of the pool. If <code>true</code> is passed as argument, the pool will never
|
||||
// * open more than {@link #getCapacity()} files at any time. If set to <code>false</code>,
|
||||
// * the pool might keep more open files, but only for a short period of time. This method might be
|
||||
// * useful if you want avoid OS limits when it comes to the number of simultaneously open files.<p>
|
||||
// *
|
||||
// * By default, the pool behaviour is 'flexible' (<code>limitedCapacity</code> property defaults
|
||||
// * to false<p>
|
||||
// *
|
||||
// * @param limitedCapacity <code>true</code> if the pool should be 'flexible' (not imposing the strict
|
||||
// * limit on the number of simultaneously open files), <code>false</code> otherwise.
|
||||
// */
|
||||
// public synchronized void setLimitedCapacity(boolean limitedCapacity) {
|
||||
// this.limitedCapacity = limitedCapacity;
|
||||
// }
|
||||
//
|
||||
// private void proveActive() throws IOException {
|
||||
// if(!active) {
|
||||
// throw new IOException("RrdDbPool is already closed");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Checks if the pool is active. You can request RrdDb references only from the active pool. The
|
||||
// * pool is deactived when the {@link #close()} method is called.
|
||||
// * @return <code>true</code> if active, <code>false</code> otherwise.
|
||||
// */
|
||||
// public synchronized boolean isActive() {
|
||||
// return active;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
@@ -1,694 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Class to represent definition of new Round Robin Database (RRD).
|
||||
* Object of this class is used to create
|
||||
* new RRD from scratch - pass its reference as a <code>RrdDb</code> constructor
|
||||
* argument (see documentation for {@link RrdDb RrdDb} class). <code>RrdDef</code>
|
||||
* object <b>does not</b> actually create new RRD. It just holds all necessary
|
||||
* information which will be used during the actual creation process.
|
||||
* <p>
|
||||
* RRD definition (RrdDef object) consists of the following elements:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li> path to RRD that will be created
|
||||
* <li> starting timestamp
|
||||
* <li> step
|
||||
* <li> one or more datasource definitions
|
||||
* <li> one or more archive definitions
|
||||
* </ul>
|
||||
* RrdDef provides API to set all these elements. For the complete explanation of all
|
||||
* RRD definition parameters, see RRDTool's
|
||||
* <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
|
||||
* <p>
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
public class RrdDef {
|
||||
/**
|
||||
* default RRD step to be used if not specified in constructor (300 seconds)
|
||||
*/
|
||||
public static final long DEFAULT_STEP = 300L;
|
||||
/**
|
||||
* if not specified in constructor, starting timestamp will be set to the
|
||||
* current timestamp plus DEFAULT_INITIAL_SHIFT seconds (-10)
|
||||
*/
|
||||
public static final long DEFAULT_INITIAL_SHIFT = -10L;
|
||||
|
||||
private String path;
|
||||
private long startTime = Util.getTime() + DEFAULT_INITIAL_SHIFT;
|
||||
private long step = DEFAULT_STEP;
|
||||
private ArrayList<DsDef> dsDefs = new ArrayList<DsDef>();
|
||||
private ArrayList<ArcDef> arcDefs = new ArrayList<ArcDef>();
|
||||
|
||||
/**
|
||||
* <p>Creates new RRD definition object with the given path.
|
||||
* When this object is passed to
|
||||
* <code>RrdDb</code> constructor, new RRD will be created using the
|
||||
* specified path. </p>
|
||||
*
|
||||
* @param path Path to new RRD.
|
||||
* @throws RrdException Thrown if name is invalid (null or empty).
|
||||
*/
|
||||
public RrdDef(final String path) throws RrdException {
|
||||
if (path == null || path.length() == 0) {
|
||||
throw new RrdException("No path specified");
|
||||
}
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates new RRD definition object with the given path and step.</p>
|
||||
*
|
||||
* @param path Path to new RRD.
|
||||
* @param step RRD step.
|
||||
* @throws RrdException Thrown if supplied parameters are invalid.
|
||||
*/
|
||||
public RrdDef(final String path, final long step) throws RrdException {
|
||||
this(path);
|
||||
if (step <= 0) {
|
||||
throw new RrdException("Invalid RRD step specified: " + step);
|
||||
}
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates new RRD definition object with the given path, starting timestamp
|
||||
* and step.</p>
|
||||
*
|
||||
* @param path Path to new RRD.
|
||||
* @param startTime RRD starting timestamp.
|
||||
* @param step RRD step.
|
||||
* @throws RrdException Thrown if supplied parameters are invalid.
|
||||
*/
|
||||
public RrdDef(final String path, final long startTime, final long step) throws RrdException {
|
||||
this(path, step);
|
||||
if (startTime < 0) {
|
||||
throw new RrdException("Invalid RRD start time specified: " + startTime);
|
||||
}
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path for the new RRD
|
||||
*
|
||||
* @return path to the new RRD which should be created
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns starting timestamp for the RRD that should be created.
|
||||
*
|
||||
* @return RRD starting timestamp
|
||||
*/
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns time step for the RRD that will be created.
|
||||
*
|
||||
* @return RRD step
|
||||
*/
|
||||
public long getStep() {
|
||||
return step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets path to RRD.
|
||||
*
|
||||
* @param path to new RRD.
|
||||
*/
|
||||
public void setPath(final String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets RRD's starting timestamp.
|
||||
*
|
||||
* @param startTime starting timestamp.
|
||||
*/
|
||||
public void setStartTime(final long startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets RRD's starting timestamp.
|
||||
*
|
||||
* @param date starting date
|
||||
*/
|
||||
public void setStartTime(final Date date) {
|
||||
this.startTime = Util.getTimestamp(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets RRD's starting timestamp.
|
||||
*
|
||||
* @param gc starting date
|
||||
*/
|
||||
public void setStartTime(final Calendar gc) {
|
||||
this.startTime = Util.getTimestamp(gc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets RRD's time step.
|
||||
*
|
||||
* @param step RRD time step.
|
||||
*/
|
||||
public void setStep(final long step) {
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds single datasource definition represented with object of class <code>DsDef</code>.
|
||||
*
|
||||
* @param dsDef Datasource definition.
|
||||
* @throws RrdException Thrown if new datasource definition uses already used data
|
||||
* source name.
|
||||
*/
|
||||
public void addDatasource(final DsDef dsDef) throws RrdException {
|
||||
if (dsDefs.contains(dsDef)) {
|
||||
throw new RrdException("Datasource already defined: " + dsDef.dump());
|
||||
}
|
||||
dsDefs.add(dsDef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds single datasource to RRD definition by specifying its data source name, source type,
|
||||
* heartbeat, minimal and maximal value. For the complete explanation of all data
|
||||
* source definition parameters see RRDTool's
|
||||
* <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
|
||||
* <p>
|
||||
* <b>IMPORTANT NOTE:</b> If datasource name ends with '!', corresponding archives will never
|
||||
* store NaNs as datasource values. In that case, NaN datasource values will be silently
|
||||
* replaced with zeros by the framework.
|
||||
*
|
||||
* @param dsName Data source name.
|
||||
* @param dsType Data source type. Valid types are "COUNTER",
|
||||
* "GAUGE", "DERIVE" and "ABSOLUTE" (these string constants are conveniently defined in
|
||||
* the {@link DsTypes} class).
|
||||
* @param heartbeat Data source heartbeat.
|
||||
* @param minValue Minimal acceptable value. Use <code>Double.NaN</code> if unknown.
|
||||
* @param maxValue Maximal acceptable value. Use <code>Double.NaN</code> if unknown.
|
||||
* @throws RrdException Thrown if new datasource definition uses already used data
|
||||
* source name.
|
||||
*/
|
||||
public void addDatasource(final String dsName, final String dsType, final long heartbeat, final double minValue, final double maxValue) throws RrdException {
|
||||
addDatasource(new DsDef(dsName, dsType, heartbeat, minValue, maxValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds single datasource to RRD definition from a RRDTool-like
|
||||
* datasource definition string. The string must have six elements separated with colons
|
||||
* (:) in the following order:
|
||||
* <p>
|
||||
* <pre>
|
||||
* DS:name:type:heartbeat:minValue:maxValue
|
||||
* </pre>
|
||||
* For example:
|
||||
* <p>
|
||||
* <pre>
|
||||
* DS:input:COUNTER:600:0:U
|
||||
* </pre>
|
||||
* For more information on datasource definition parameters see <code>rrdcreate</code>
|
||||
* man page.
|
||||
*
|
||||
* @param rrdToolDsDef Datasource definition string with the syntax borrowed from RRDTool.
|
||||
* @throws RrdException Thrown if invalid string is supplied.
|
||||
*/
|
||||
public void addDatasource(final String rrdToolDsDef) throws RrdException {
|
||||
final RrdException rrdException = new RrdException("Wrong rrdtool-like datasource definition: " + rrdToolDsDef);
|
||||
final StringTokenizer tokenizer = new StringTokenizer(rrdToolDsDef, ":");
|
||||
if (tokenizer.countTokens() != 6) {
|
||||
throw rrdException;
|
||||
}
|
||||
final String[] tokens = new String[6];
|
||||
for (int curTok = 0; tokenizer.hasMoreTokens(); curTok++) {
|
||||
tokens[curTok] = tokenizer.nextToken();
|
||||
}
|
||||
if (!tokens[0].equalsIgnoreCase("DS")) {
|
||||
throw rrdException;
|
||||
}
|
||||
final String dsName = tokens[1];
|
||||
final String dsType = tokens[2];
|
||||
long dsHeartbeat;
|
||||
try {
|
||||
dsHeartbeat = Long.parseLong(tokens[3]);
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
throw rrdException;
|
||||
}
|
||||
double minValue = Double.NaN;
|
||||
if (!tokens[4].equalsIgnoreCase("U")) {
|
||||
try {
|
||||
minValue = Double.parseDouble(tokens[4]);
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
throw rrdException;
|
||||
}
|
||||
}
|
||||
double maxValue = Double.NaN;
|
||||
if (!tokens[5].equalsIgnoreCase("U")) {
|
||||
try {
|
||||
maxValue = Double.parseDouble(tokens[5]);
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
throw rrdException;
|
||||
}
|
||||
}
|
||||
addDatasource(new DsDef(dsName, dsType, dsHeartbeat, minValue, maxValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds data source definitions to RRD definition in bulk.
|
||||
*
|
||||
* @param dsDefs Array of data source definition objects.
|
||||
* @throws RrdException Thrown if duplicate data source name is used.
|
||||
*/
|
||||
public void addDatasource(final DsDef[] dsDefs) throws RrdException {
|
||||
for (final DsDef dsDef : dsDefs) {
|
||||
addDatasource(dsDef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds single archive definition represented with object of class <code>ArcDef</code>.
|
||||
*
|
||||
* @param arcDef Archive definition.
|
||||
* @throws RrdException Thrown if archive with the same consolidation function
|
||||
* and the same number of steps is already added.
|
||||
*/
|
||||
public void addArchive(final ArcDef arcDef) throws RrdException {
|
||||
if (arcDefs.contains(arcDef)) {
|
||||
throw new RrdException("Archive already defined: " + arcDef.dump());
|
||||
}
|
||||
arcDefs.add(arcDef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds archive definitions to RRD definition in bulk.
|
||||
*
|
||||
* @param arcDefs Array of archive definition objects
|
||||
* @throws RrdException Thrown if RRD definition already contains archive with
|
||||
* the same consolidation function and the same number of steps.
|
||||
*/
|
||||
public void addArchive(final ArcDef[] arcDefs) throws RrdException {
|
||||
for (final ArcDef arcDef : arcDefs) {
|
||||
addArchive(arcDef);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds single archive definition by specifying its consolidation function, X-files factor,
|
||||
* number of steps and rows. For the complete explanation of all archive
|
||||
* definition parameters see RRDTool's
|
||||
* <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
|
||||
* <p>
|
||||
*
|
||||
* @param consolFun Consolidation function. Valid values are "AVERAGE",
|
||||
* "MIN", "MAX" and "LAST" (these constants are conveniently defined in the
|
||||
* {@link ConsolFuns} class)
|
||||
* @param xff X-files factor. Valid values are between 0 and 1.
|
||||
* @param steps Number of archive steps
|
||||
* @param rows Number of archive rows
|
||||
* @throws RrdException Thrown if archive with the same consolidation function
|
||||
* and the same number of steps is already added.
|
||||
*/
|
||||
public void addArchive(final String consolFun, final double xff, final int steps, final int rows) throws RrdException {
|
||||
addArchive(new ArcDef(consolFun, xff, steps, rows));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds single archive to RRD definition from a RRDTool-like
|
||||
* archive definition string. The string must have five elements separated with colons
|
||||
* (:) in the following order:
|
||||
* <p>
|
||||
* <pre>
|
||||
* RRA:consolidationFunction:XFilesFactor:steps:rows
|
||||
* </pre>
|
||||
* For example:
|
||||
* <p>
|
||||
* <pre>
|
||||
* RRA:AVERAGE:0.5:10:1000
|
||||
* </pre>
|
||||
* For more information on archive definition parameters see <code>rrdcreate</code>
|
||||
* man page.
|
||||
*
|
||||
* @param rrdToolArcDef Archive definition string with the syntax borrowed from RRDTool.
|
||||
* @throws RrdException Thrown if invalid string is supplied.
|
||||
*/
|
||||
public void addArchive(final String rrdToolArcDef) throws RrdException {
|
||||
final RrdException rrdException = new RrdException("Wrong rrdtool-like archive definition: " + rrdToolArcDef);
|
||||
final StringTokenizer tokenizer = new StringTokenizer(rrdToolArcDef, ":");
|
||||
if (tokenizer.countTokens() != 5) {
|
||||
throw rrdException;
|
||||
}
|
||||
final String[] tokens = new String[5];
|
||||
for (int curTok = 0; tokenizer.hasMoreTokens(); curTok++) {
|
||||
tokens[curTok] = tokenizer.nextToken();
|
||||
}
|
||||
if (!tokens[0].equalsIgnoreCase("RRA")) {
|
||||
throw rrdException;
|
||||
}
|
||||
final String consolFun = tokens[1];
|
||||
double xff;
|
||||
try {
|
||||
xff = Double.parseDouble(tokens[2]);
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
throw rrdException;
|
||||
}
|
||||
int steps;
|
||||
try {
|
||||
steps = Integer.parseInt(tokens[3]);
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
throw rrdException;
|
||||
}
|
||||
int rows;
|
||||
try {
|
||||
rows = Integer.parseInt(tokens[4]);
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
throw rrdException;
|
||||
}
|
||||
addArchive(new ArcDef(consolFun, xff, steps, rows));
|
||||
}
|
||||
|
||||
void validate() throws RrdException {
|
||||
if (dsDefs.size() == 0) {
|
||||
throw new RrdException("No RRD datasource specified. At least one is needed.");
|
||||
}
|
||||
if (arcDefs.size() == 0) {
|
||||
throw new RrdException("No RRD archive specified. At least one is needed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all data source definition objects specified so far.
|
||||
*
|
||||
* @return Array of data source definition objects
|
||||
*/
|
||||
public DsDef[] getDsDefs() {
|
||||
return dsDefs.toArray(new DsDef[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all archive definition objects specified so far.
|
||||
*
|
||||
* @return Array of archive definition objects.
|
||||
*/
|
||||
public ArcDef[] getArcDefs() {
|
||||
return arcDefs.toArray(new ArcDef[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of defined datasources.
|
||||
*
|
||||
* @return Number of defined datasources.
|
||||
*/
|
||||
public int getDsCount() {
|
||||
return dsDefs.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of defined archives.
|
||||
*
|
||||
* @return Number of defined archives.
|
||||
*/
|
||||
public int getArcCount() {
|
||||
return arcDefs.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string that represents all specified RRD creation parameters. Returned string
|
||||
* has the syntax of RRDTool's <code>create</code> command.
|
||||
*
|
||||
* @return Dumped content of <code>RrdDb</code> object.
|
||||
*/
|
||||
public String dump() {
|
||||
final StringBuffer buffer = new StringBuffer("create \"");
|
||||
buffer.append(path).append("\"");
|
||||
buffer.append(" --start ").append(getStartTime());
|
||||
buffer.append(" --step ").append(getStep()).append(" ");
|
||||
for (final DsDef dsDef : dsDefs) {
|
||||
buffer.append(dsDef.dump()).append(" ");
|
||||
}
|
||||
for (final ArcDef arcDef : arcDefs) {
|
||||
buffer.append(arcDef.dump()).append(" ");
|
||||
}
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
|
||||
String getRrdToolCommand() {
|
||||
return dump();
|
||||
}
|
||||
|
||||
void removeDatasource(final String dsName) throws RrdException {
|
||||
for (int i = 0; i < dsDefs.size(); i++) {
|
||||
final DsDef dsDef = dsDefs.get(i);
|
||||
if (dsDef.getDsName().equals(dsName)) {
|
||||
dsDefs.remove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RrdException("Could not find datasource named '" + dsName + "'");
|
||||
}
|
||||
|
||||
void saveSingleDatasource(final String dsName) {
|
||||
final Iterator<DsDef> it = dsDefs.iterator();
|
||||
while (it.hasNext()) {
|
||||
final DsDef dsDef = it.next();
|
||||
if (!dsDef.getDsName().equals(dsName)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeArchive(final String consolFun, final int steps) throws RrdException {
|
||||
final ArcDef arcDef = findArchive(consolFun, steps);
|
||||
if (!arcDefs.remove(arcDef)) {
|
||||
throw new RrdException("Could not remove archive " + consolFun + "/" + steps);
|
||||
}
|
||||
}
|
||||
|
||||
ArcDef findArchive(final String consolFun, final int steps) throws RrdException {
|
||||
for (final ArcDef arcDef : arcDefs) {
|
||||
if (arcDef.getConsolFun().equals(consolFun) && arcDef.getSteps() == steps) {
|
||||
return arcDef;
|
||||
}
|
||||
}
|
||||
throw new RrdException("Could not find archive " + consolFun + "/" + steps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports RrdDef object to output stream in XML format. Generated XML code can be parsed
|
||||
* with {@link RrdDefTemplate} class.
|
||||
*
|
||||
* @param out Output stream
|
||||
*/
|
||||
public void exportXmlTemplate(final OutputStream out) {
|
||||
final XmlWriter xml = new XmlWriter(out);
|
||||
xml.startTag("rrd_def");
|
||||
xml.writeTag("path", getPath());
|
||||
xml.writeTag("step", getStep());
|
||||
xml.writeTag("start", getStartTime());
|
||||
for (final DsDef dsDef : getDsDefs()) {
|
||||
xml.startTag("datasource");
|
||||
xml.writeTag("name", dsDef.getDsName());
|
||||
xml.writeTag("type", dsDef.getDsType());
|
||||
xml.writeTag("heartbeat", dsDef.getHeartbeat());
|
||||
xml.writeTag("min", dsDef.getMinValue(), "U");
|
||||
xml.writeTag("max", dsDef.getMaxValue(), "U");
|
||||
xml.closeTag(); // datasource
|
||||
}
|
||||
for (ArcDef arcDef : getArcDefs()) {
|
||||
xml.startTag("archive");
|
||||
xml.writeTag("cf", arcDef.getConsolFun());
|
||||
xml.writeTag("xff", arcDef.getXff());
|
||||
xml.writeTag("steps", arcDef.getSteps());
|
||||
xml.writeTag("rows", arcDef.getRows());
|
||||
xml.closeTag(); // archive
|
||||
}
|
||||
xml.closeTag(); // rrd_def
|
||||
xml.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports RrdDef object to string in XML format. Generated XML string can be parsed
|
||||
* with {@link RrdDefTemplate} class.
|
||||
*
|
||||
* @return XML formatted string representing this RrdDef object
|
||||
*/
|
||||
public String exportXmlTemplate() {
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
exportXmlTemplate(out);
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports RrdDef object to a file in XML format. Generated XML code can be parsed
|
||||
* with {@link RrdDefTemplate} class.
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void exportXmlTemplate(final String filePath) throws IOException {
|
||||
final FileOutputStream out = new FileOutputStream(filePath, false);
|
||||
exportXmlTemplate(out);
|
||||
out.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of storage bytes required to create RRD from this
|
||||
* RrdDef object.
|
||||
*
|
||||
* @return Estimated byte count of the underlying RRD storage.
|
||||
*/
|
||||
public long getEstimatedSize() {
|
||||
final int dsCount = dsDefs.size();
|
||||
final int arcCount = arcDefs.size();
|
||||
int rowsCount = 0;
|
||||
for (final ArcDef arcDef : arcDefs) {
|
||||
rowsCount += arcDef.getRows();
|
||||
}
|
||||
return calculateSize(dsCount, arcCount, rowsCount);
|
||||
}
|
||||
|
||||
static long calculateSize(final int dsCount, final int arcCount, final int rowsCount) {
|
||||
return (24L + 48L * dsCount + 16L * arcCount +
|
||||
20L * dsCount * arcCount + 8L * dsCount * rowsCount) +
|
||||
(1L + 2L * dsCount + arcCount) * 2L * RrdPrimitive.STRING_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the current RrdDef with another. RrdDefs are considered equal if:<p>
|
||||
* <ul>
|
||||
* <li>RRD steps match
|
||||
* <li>all datasources have exactly the same definition in both RrdDef objects (datasource names,
|
||||
* types, heartbeat, min and max values must match)
|
||||
* <li>all archives have exactly the same definition in both RrdDef objects (archive consolidation
|
||||
* functions, X-file factors, step and row counts must match)
|
||||
* </ul>
|
||||
*
|
||||
* @param obj The second RrdDef object
|
||||
* @return true if RrdDefs match exactly, false otherwise
|
||||
*/
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == null || !(obj instanceof RrdDef)) {
|
||||
return false;
|
||||
}
|
||||
final RrdDef rrdDef2 = (RrdDef) obj;
|
||||
// check primary RRD step
|
||||
if (step != rrdDef2.step) {
|
||||
return false;
|
||||
}
|
||||
// check datasources
|
||||
final DsDef[] dsDefs = getDsDefs();
|
||||
final DsDef[] dsDefs2 = rrdDef2.getDsDefs();
|
||||
if (dsDefs.length != dsDefs2.length) {
|
||||
return false;
|
||||
}
|
||||
for (final DsDef dsDef : dsDefs) {
|
||||
boolean matched = false;
|
||||
for (final DsDef dsDef2 : dsDefs2) {
|
||||
if (dsDef.exactlyEqual(dsDef2)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// this datasource could not be matched
|
||||
if (!matched) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// check archives
|
||||
final ArcDef[] arcDefs = getArcDefs();
|
||||
final ArcDef[] arcDefs2 = rrdDef2.getArcDefs();
|
||||
if (arcDefs.length != arcDefs2.length) {
|
||||
return false;
|
||||
}
|
||||
for (final ArcDef arcDef : arcDefs) {
|
||||
boolean matched = false;
|
||||
for (final ArcDef arcDef2 : arcDefs2) {
|
||||
if (arcDef.exactlyEqual(arcDef2)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// this archive could not be matched
|
||||
if (!matched) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// everything matches
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hashCode = (int)step;
|
||||
for (final DsDef dsDef : dsDefs) {
|
||||
hashCode *= dsDef.hashCode();
|
||||
}
|
||||
for (final ArcDef arcDef : arcDefs) {
|
||||
hashCode *= arcDef.hashCode();
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all datasource definitions.
|
||||
*/
|
||||
public void removeDatasources() {
|
||||
dsDefs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all RRA archive definitions.
|
||||
*/
|
||||
public void removeArchives() {
|
||||
arcDefs.clear();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[arcDefs=[" + join(getArcDefs()) + "],dsDefs=[" + join(getDsDefs()) + "]]";
|
||||
}
|
||||
|
||||
private String join(final Object[] objs) {
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < objs.length; i++) {
|
||||
sb.append(objs[i]);
|
||||
if (i != (objs.length - 1)) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Class used to create an arbitrary number of {@link RrdDef} (RRD definition) objects
|
||||
* from a single XML template. XML template can be supplied as an XML InputSource,
|
||||
* XML file or XML formatted string.
|
||||
* <p>
|
||||
* Here is an example of a properly formatted XML template with all available
|
||||
* options in it (unwanted options can be removed):
|
||||
* <pre>
|
||||
* <rrd_def>
|
||||
* <path>test.rrd</path>
|
||||
* <!-- not mandatory -->
|
||||
* <start>1000123456</start>
|
||||
* <!-- not mandatory -->
|
||||
* <step>300</step>
|
||||
* <!-- at least one datasource must be supplied -->
|
||||
* <datasource>
|
||||
* <name>input</name>
|
||||
* <type>COUNTER</type>
|
||||
* <heartbeat>300</heartbeat>
|
||||
* <min>0</min>
|
||||
* <max>U</max>
|
||||
* </datasource>
|
||||
* <datasource>
|
||||
* <name>temperature</name>
|
||||
* <type>GAUGE</type>
|
||||
* <heartbeat>400</heartbeat>
|
||||
* <min>U</min>
|
||||
* <max>1000</max>
|
||||
* </datasource>
|
||||
* <!-- at least one archive must be supplied -->
|
||||
* <archive>
|
||||
* <cf>AVERAGE</cf>
|
||||
* <xff>0.5</xff>
|
||||
* <steps>1</steps>
|
||||
* <rows>600</rows>
|
||||
* </archive>
|
||||
* <archive>
|
||||
* <cf>MAX</cf>
|
||||
* <xff>0.6</xff>
|
||||
* <steps>6</steps>
|
||||
* <rows>7000</rows>
|
||||
* </archive>
|
||||
* </rrd_def>
|
||||
* </pre>
|
||||
* Notes on the template syntax:<p>
|
||||
* <ul>
|
||||
* <li>There is a strong relation between the XML template syntax and the syntax of
|
||||
* {@link RrdDef} class methods. If you are not sure what some XML tag means, check javadoc
|
||||
* for the corresponding class.
|
||||
* <li>starting timestamp can be supplied either as a long integer
|
||||
* (like: 1000243567) or as an ISO formatted string (like: 2004-02-21 12:25:45)
|
||||
* <li>whitespaces are not harmful
|
||||
* <li>floating point values: anything that cannot be parsed will be treated as Double.NaN
|
||||
* (like: U, unknown, 12r.23)
|
||||
* <li>comments are allowed.
|
||||
* </ul>
|
||||
* Any template value (text between <code><some_tag></code> and
|
||||
* <code></some_tag></code>) can be replaced with
|
||||
* a variable of the following form: <code>${variable_name}</code>. Use
|
||||
* {@link XmlTemplate#setVariable(String, String) setVariable()}
|
||||
* methods from the base class to replace template variables with real values
|
||||
* at runtime.
|
||||
* <p>
|
||||
* Typical usage scenario:<p>
|
||||
* <ul>
|
||||
* <li>Create your XML template and save it to a file (template.xml, for example)
|
||||
* <li>Replace hardcoded template values with variables if you want to change them during runtime.
|
||||
* For example, RRD path should not be hardcoded in the template - you probably want to create
|
||||
* many different RRD files from the same XML template. For example, your XML
|
||||
* template could start with:
|
||||
* <pre>
|
||||
* <rrd_def>
|
||||
* <path>${path}</path>
|
||||
* <step>300</step>
|
||||
* ...
|
||||
* </pre>
|
||||
* <li>In your Java code, create RrdDefTemplate object using your XML template file:
|
||||
* <pre>
|
||||
* RrdDefTemplate t = new RrdDefTemplate(new File(template.xml));
|
||||
* </pre>
|
||||
* <li>Then, specify real values for template variables:
|
||||
* <pre>
|
||||
* t.setVariable("path", "demo/test.rrd");
|
||||
* </pre>
|
||||
* <li>Once all template variables are set, just use the template object to create RrdDef
|
||||
* object. This object is actually used to create JRobin RRD files:
|
||||
* <pre>
|
||||
* RrdDef def = t.getRrdDef();
|
||||
* RrdDb rrd = new RrdDb(def);
|
||||
* rrd.close();
|
||||
* </pre>
|
||||
* </ul>
|
||||
* You should create new RrdDefTemplate object only once for each XML template. Single template
|
||||
* object can be reused to create as many RrdDef objects as needed, with different values
|
||||
* specified for template variables. XML synatax check is performed only once - the first
|
||||
* definition object gets created relatively slowly, but it will be created much faster next time.
|
||||
*/
|
||||
public class RrdDefTemplate extends XmlTemplate {
|
||||
/**
|
||||
* Creates RrdDefTemplate object from any parsable XML input source. Read general information
|
||||
* for this class to find an example of a properly formatted RrdDef XML source.
|
||||
*
|
||||
* @param xmlInputSource Xml input source
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of XML related error (parsing error, for example)
|
||||
*/
|
||||
public RrdDefTemplate(InputSource xmlInputSource) throws IOException, RrdException {
|
||||
super(xmlInputSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates RrdDefTemplate object from the string containing XML template.
|
||||
* Read general information for this class to see an example of a properly formatted XML source.
|
||||
*
|
||||
* @param xmlString String containing XML template
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of XML related error (parsing error, for example)
|
||||
*/
|
||||
public RrdDefTemplate(String xmlString) throws IOException, RrdException {
|
||||
super(xmlString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates RrdDefTemplate object from the file containing XML template.
|
||||
* Read general information for this class to see an example of a properly formatted XML source.
|
||||
*
|
||||
* @param xmlFile File object representing file with XML template
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of XML related error (parsing error, for example)
|
||||
*/
|
||||
public RrdDefTemplate(File xmlFile) throws IOException, RrdException {
|
||||
super(xmlFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns RrdDef object constructed from the underlying XML template. Before this method
|
||||
* is called, values for all non-optional placeholders must be supplied. To specify
|
||||
* placeholder values at runtime, use some of the overloaded
|
||||
* {@link XmlTemplate#setVariable(String, String) setVariable()} methods. Once this method
|
||||
* returns, all placeholder values are preserved. To remove them all, call inhereted
|
||||
* {@link XmlTemplate#clearValues() clearValues()} method explicitly.<p>
|
||||
*
|
||||
* @return RrdDef object constructed from the underlying XML template,
|
||||
* with all placeholders replaced with real values. This object can be passed to the constructor
|
||||
* of the new RrdDb object.
|
||||
* @throws RrdException Thrown (in most cases) if the value for some placeholder
|
||||
* was not supplied through {@link XmlTemplate#setVariable(String, String) setVariable()}
|
||||
* method call
|
||||
*/
|
||||
public RrdDef getRrdDef() throws RrdException {
|
||||
if (!root.getTagName().equals("rrd_def")) {
|
||||
throw new RrdException("XML definition must start with <rrd_def>");
|
||||
}
|
||||
validateTagsOnlyOnce(root, new String[] {
|
||||
"path", "start", "step", "datasource*", "archive*"
|
||||
});
|
||||
// PATH must be supplied or exception is thrown
|
||||
String path = getChildValue(root, "path");
|
||||
RrdDef rrdDef = new RrdDef(path);
|
||||
try {
|
||||
String startStr = getChildValue(root, "start");
|
||||
Calendar startGc = Util.getCalendar(startStr);
|
||||
rrdDef.setStartTime(startGc);
|
||||
}
|
||||
catch (RrdException e) {
|
||||
// START is not mandatory
|
||||
}
|
||||
try {
|
||||
long step = getChildValueAsLong(root, "step");
|
||||
rrdDef.setStep(step);
|
||||
}
|
||||
catch (RrdException e) {
|
||||
// STEP is not mandatory
|
||||
}
|
||||
// datsources
|
||||
Node[] dsNodes = getChildNodes(root, "datasource");
|
||||
for (Node dsNode : dsNodes) {
|
||||
validateTagsOnlyOnce(dsNode, new String[] {
|
||||
"name", "type", "heartbeat", "min", "max"
|
||||
});
|
||||
String name = getChildValue(dsNode, "name");
|
||||
String type = getChildValue(dsNode, "type");
|
||||
long heartbeat = getChildValueAsLong(dsNode, "heartbeat");
|
||||
double min = getChildValueAsDouble(dsNode, "min");
|
||||
double max = getChildValueAsDouble(dsNode, "max");
|
||||
rrdDef.addDatasource(name, type, heartbeat, min, max);
|
||||
}
|
||||
// archives
|
||||
Node[] arcNodes = getChildNodes(root, "archive");
|
||||
for (Node arcNode : arcNodes) {
|
||||
validateTagsOnlyOnce(arcNode, new String[] {
|
||||
"cf", "xff", "steps", "rows"
|
||||
});
|
||||
String consolFun = getChildValue(arcNode, "cf");
|
||||
double xff = getChildValueAsDouble(arcNode, "xff");
|
||||
int steps = getChildValueAsInt(arcNode, "steps");
|
||||
int rows = getChildValueAsInt(arcNode, "rows");
|
||||
rrdDef.addArchive(consolFun, xff, steps, rows);
|
||||
}
|
||||
return rrdDef;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class RrdDouble extends RrdPrimitive {
|
||||
private double cache;
|
||||
private boolean cached = false;
|
||||
|
||||
RrdDouble(final RrdUpdater updater, final boolean isConstant) throws IOException {
|
||||
super(updater, RrdDouble.RRD_DOUBLE, isConstant);
|
||||
}
|
||||
|
||||
RrdDouble(final RrdUpdater updater) throws IOException {
|
||||
super(updater, RrdDouble.RRD_DOUBLE, false);
|
||||
}
|
||||
|
||||
void set(final double value) throws IOException {
|
||||
if (!isCachingAllowed()) {
|
||||
writeDouble(value);
|
||||
}
|
||||
// caching allowed
|
||||
else if (!cached || !Util.equal(cache, value)) {
|
||||
// update cache
|
||||
writeDouble(cache = value);
|
||||
cached = true;
|
||||
}
|
||||
}
|
||||
|
||||
double get() throws IOException {
|
||||
return cached ? cache : readDouble();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class RrdDoubleArray extends RrdPrimitive {
|
||||
private int length;
|
||||
|
||||
RrdDoubleArray(final RrdUpdater updater, final int length) throws IOException {
|
||||
super(updater, RrdPrimitive.RRD_DOUBLE, length, false);
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
void set(final int index, final double value) throws IOException {
|
||||
set(index, value, 1);
|
||||
}
|
||||
|
||||
void set(final int index, final double value, final int count) throws IOException {
|
||||
// rollovers not allowed!
|
||||
assert index + count <= length: "Invalid robin index supplied: index=" + index +", count=" + count + ", length=" + length;
|
||||
writeDouble(index, value, count);
|
||||
}
|
||||
|
||||
double get(final int index) throws IOException {
|
||||
assert index < length: "Invalid index supplied: " + index + ", length=" + length;
|
||||
return readDouble(index);
|
||||
}
|
||||
|
||||
double[] get(final int index, final int count) throws IOException {
|
||||
assert index + count <= length: "Invalid index/count supplied: " + index + "/" + count + " (length=" + length + ")";
|
||||
return readDouble(index, count);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
/**
|
||||
* Class to represent various JRobin checked exceptions.
|
||||
* JRobin code can throw only <code>RrdException</code>
|
||||
* (for various JRobin related errors) or <code>IOException</code>
|
||||
* (for various I/O errors).
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
public class RrdException extends Exception {
|
||||
private static final long serialVersionUID = 6999702149227009855L;
|
||||
|
||||
public RrdException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RrdException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public RrdException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public RrdException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
/**
|
||||
* JRobin backend which is used to store RRD data to ordinary files on the disk. This was the
|
||||
* default factory before 1.4.0 version.
|
||||
* <p>
|
||||
* This backend is based on the RandomAccessFile class (java.io.* package).
|
||||
*/
|
||||
public class RrdFileBackend extends RrdBackend {
|
||||
/**
|
||||
* radnom access file handle
|
||||
*/
|
||||
protected RandomAccessFile file;
|
||||
|
||||
/**
|
||||
* Creates RrdFileBackend object for the given file path, backed by RandomAccessFile object.
|
||||
*
|
||||
* @param path Path to a file
|
||||
* @param readOnly True, if file should be open in a read-only mode. False otherwise
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
protected RrdFileBackend(final String path, final boolean readOnly) throws IOException {
|
||||
super(path, readOnly);
|
||||
this.file = new RandomAccessFile(path, readOnly ? "r" : "rw");
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying RRD file.
|
||||
*
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
file.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns canonical path to the file on the disk.
|
||||
*
|
||||
* @param path File path
|
||||
* @return Canonical file path
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public static String getCanonicalPath(String path) throws IOException {
|
||||
return Util.getCanonicalPath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns canonical path to the file on the disk.
|
||||
*
|
||||
* @return Canonical file path
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public String getCanonicalPath() throws IOException {
|
||||
return RrdFileBackend.getCanonicalPath(getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes bytes to the underlying RRD file on the disk
|
||||
*
|
||||
* @param offset Starting file offset
|
||||
* @param b Bytes to be written.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
protected void write(long offset, byte[] b) throws IOException {
|
||||
file.seek(offset);
|
||||
file.write(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a number of bytes from the RRD file on the disk
|
||||
*
|
||||
* @param offset Starting file offset
|
||||
* @param b Buffer which receives bytes read from the file.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected void read(long offset, byte[] b) throws IOException {
|
||||
file.seek(offset);
|
||||
if (file.read(b) != b.length) {
|
||||
throw new IOException("Not enough bytes available in file " + getPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns RRD file length.
|
||||
*
|
||||
* @return File length.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public long getLength() throws IOException {
|
||||
return file.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets length of the underlying RRD file. This method is called only once, immediately
|
||||
* after a new RRD file gets created.
|
||||
*
|
||||
* @param length Length of the RRD file
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected void setLength(long length) throws IOException {
|
||||
file.setLength(length);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Factory class which creates actual {@link RrdFileBackend} objects. This was the default
|
||||
* backend factory in JRobin before 1.4.0 release.
|
||||
*/
|
||||
public class RrdFileBackendFactory extends RrdBackendFactory {
|
||||
/**
|
||||
* factory name, "FILE"
|
||||
*/
|
||||
public static final String NAME = "FILE";
|
||||
|
||||
/**
|
||||
* Creates RrdFileBackend object for the given file path.
|
||||
*
|
||||
* @param path File path
|
||||
* @param readOnly True, if the file should be accessed in read/only mode.
|
||||
* False otherwise.
|
||||
* @return RrdFileBackend object which handles all I/O operations for the given file path
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected RrdBackend open(String path, boolean readOnly) throws IOException {
|
||||
return new RrdFileBackend(path, readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if a file with the given path already exists.
|
||||
*
|
||||
* @param path File path
|
||||
* @return True, if such file exists, false otherwise.
|
||||
*/
|
||||
protected boolean exists(String path) {
|
||||
return Util.fileExists(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this factory.
|
||||
*
|
||||
* @return Factory name (equals to string "FILE")
|
||||
*/
|
||||
public String getFactoryName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class RrdInt extends RrdPrimitive {
|
||||
private int cache;
|
||||
private boolean cached = false;
|
||||
|
||||
RrdInt(final RrdUpdater updater, final boolean isConstant) throws IOException {
|
||||
super(updater, RrdPrimitive.RRD_INT, isConstant);
|
||||
}
|
||||
|
||||
RrdInt(final RrdUpdater updater) throws IOException {
|
||||
this(updater, false);
|
||||
}
|
||||
|
||||
void set(final int value) throws IOException {
|
||||
if (!isCachingAllowed()) {
|
||||
writeInt(value);
|
||||
}
|
||||
// caching allowed
|
||||
else if (!cached || cache != value) {
|
||||
// update cache
|
||||
writeInt(cache = value);
|
||||
cached = true;
|
||||
}
|
||||
}
|
||||
|
||||
int get() throws IOException {
|
||||
return cached ? cache : readInt();
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
/* ============================================================
|
||||
* JRobin : Pure java implementation of RRDTool's functionality
|
||||
* ============================================================
|
||||
*
|
||||
* Project Info: http://www.jrobin.org
|
||||
* Project Lead: Sasa Markovic (saxon@jrobin.org);
|
||||
*
|
||||
* (C) Copyright 2003, by Sasa Markovic.
|
||||
*
|
||||
* Developers: Sasa Markovic (saxon@jrobin.org)
|
||||
* Arne Vandamme (cobralord@jrobin.org)
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Foundation;
|
||||
* either version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with this
|
||||
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* JRobin backend which is used to store RRD data to ordinary files on the disk. This was the
|
||||
* default factory before 1.4.0 version.
|
||||
* <p>
|
||||
* This backend is based on the RandomAccessFile class (java.io.* package).
|
||||
*/
|
||||
public class RrdJRobin14FileBackend extends RrdBackend {
|
||||
private static final long LOCK_DELAY = 100; // 0.1sec
|
||||
|
||||
private static Set<String> m_openFiles = new HashSet<String>();
|
||||
|
||||
public static enum LockMode {
|
||||
EXCEPTION_IF_LOCKED,
|
||||
WAIT_IF_LOCKED,
|
||||
NO_LOCKS
|
||||
};
|
||||
|
||||
/** locking mode */
|
||||
protected LockMode m_lockMode;
|
||||
|
||||
/** random access file handle */
|
||||
protected RandomAccessFile m_file;
|
||||
/** file lock */
|
||||
protected FileLock m_fileLock;
|
||||
|
||||
/**
|
||||
* Creates RrdFileBackend object for the given file path, backed by RandomAccessFile object.
|
||||
* @param path Path to a file
|
||||
* @param readOnly True, if file should be open in a read-only mode. False otherwise
|
||||
* @param lockMode Locking mode: Exception if locked, Wait if locked, or no locks.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
protected RrdJRobin14FileBackend(String path, boolean readOnly, LockMode lockMode) throws IOException {
|
||||
super(path, readOnly);
|
||||
m_lockMode = lockMode;
|
||||
m_file = new RandomAccessFile(path, readOnly ? "r" : "rw");
|
||||
try {
|
||||
lockFile();
|
||||
registerWriter();
|
||||
} catch(final IOException ioe) {
|
||||
close();
|
||||
throw ioe;
|
||||
}
|
||||
System.err.println(String.format("backend initialized with path=%s, readOnly=%s, lockMode=%s", path, Boolean.valueOf(readOnly), lockMode));
|
||||
}
|
||||
|
||||
private void lockFile() throws IOException {
|
||||
switch (m_lockMode) {
|
||||
case EXCEPTION_IF_LOCKED:
|
||||
m_fileLock = m_file.getChannel().tryLock();
|
||||
if (m_fileLock == null) {
|
||||
// could not obtain lock
|
||||
throw new IOException("Access denied. " + "File [" + getPath() + "] already locked");
|
||||
}
|
||||
break;
|
||||
case WAIT_IF_LOCKED:
|
||||
while (m_fileLock == null) {
|
||||
m_fileLock = m_file.getChannel().tryLock();
|
||||
if (m_fileLock == null) {
|
||||
// could not obtain lock, wait a little, than try again
|
||||
try {
|
||||
Thread.sleep(LOCK_DELAY);
|
||||
}
|
||||
catch (final InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NO_LOCKS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void registerWriter() throws IOException {
|
||||
if (!isReadOnly()) {
|
||||
final String canonicalPath = Util.getCanonicalPath(getPath());
|
||||
synchronized (m_openFiles) {
|
||||
if (m_openFiles.contains(canonicalPath)) {
|
||||
throw new IOException("File \"" + getPath() + "\" already open for R/W access. " +
|
||||
"You cannot open the same file for R/W access twice");
|
||||
}
|
||||
else {
|
||||
m_openFiles.add(canonicalPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying RRD file.
|
||||
*
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
unregisterWriter();
|
||||
try {
|
||||
unlockFile();
|
||||
} finally {
|
||||
m_file.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void unlockFile() throws IOException {
|
||||
if (m_fileLock != null) {
|
||||
m_fileLock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterWriter() throws IOException {
|
||||
if (!isReadOnly()) {
|
||||
synchronized (m_openFiles) {
|
||||
m_openFiles.remove(Util.getCanonicalPath(getPath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns canonical path to the file on the disk.
|
||||
*
|
||||
* @return Canonical file path
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public String getCanonicalPath() throws IOException {
|
||||
return Util.getCanonicalPath(getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes bytes to the underlying RRD file on the disk
|
||||
*
|
||||
* @param offset Starting file offset
|
||||
* @param b Bytes to be written.
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
protected void write(final long offset, final byte[] b) throws IOException {
|
||||
m_file.seek(offset);
|
||||
m_file.write(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a number of bytes from the RRD file on the disk
|
||||
*
|
||||
* @param offset Starting file offset
|
||||
* @param b Buffer which receives bytes read from the file.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected void read(final long offset, final byte[] b) throws IOException {
|
||||
m_file.seek(offset);
|
||||
if (m_file.read(b) != b.length) {
|
||||
throw new IOException("Not enough bytes available in file " + getPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns RRD file length.
|
||||
*
|
||||
* @return File length.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
public long getLength() throws IOException {
|
||||
return m_file.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets length of the underlying RRD file. This method is called only once, immediately
|
||||
* after a new RRD file gets created.
|
||||
*
|
||||
* @param length Length of the RRD file
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected void setLength(final long length) throws IOException {
|
||||
m_file.setLength(length);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jrobin.core.RrdJRobin14FileBackend.LockMode;
|
||||
|
||||
/**
|
||||
* Factory class which creates actual {@link RrdFileBackend} objects. This was the default
|
||||
* backend factory in JRobin before 1.4.0 release.
|
||||
*/
|
||||
public class RrdJRobin14FileBackendFactory extends RrdBackendFactory {
|
||||
/**
|
||||
* factory name, "FILE"
|
||||
*/
|
||||
public static final String NAME = "14FILE";
|
||||
private LockMode m_lockMode = LockMode.NO_LOCKS;
|
||||
|
||||
public RrdJRobin14FileBackendFactory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RrdJRobin14FileBackendFactory(final LockMode lockMode) {
|
||||
super();
|
||||
m_lockMode = lockMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates RrdFileBackend object for the given file path.
|
||||
*
|
||||
* @param path File path
|
||||
* @param readOnly True, if the file should be accessed in read/only mode.
|
||||
* False otherwise.
|
||||
* @return RrdFileBackend object which handles all I/O operations for the given file path
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected RrdBackend open(final String path, final boolean readOnly) throws IOException {
|
||||
return new RrdJRobin14FileBackend(path, readOnly, m_lockMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if a file with the given path already exists.
|
||||
*
|
||||
* @param path File path
|
||||
* @return True, if such file exists, false otherwise.
|
||||
*/
|
||||
protected boolean exists(final String path) {
|
||||
return Util.fileExists(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this factory.
|
||||
*
|
||||
* @return Factory name (equals to string "FILE")
|
||||
*/
|
||||
public String getFactoryName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[name=" + getFactoryName() + ",lockMode=" + m_lockMode + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class RrdLong extends RrdPrimitive {
|
||||
private long cache;
|
||||
private boolean cached = false;
|
||||
|
||||
RrdLong(final RrdUpdater updater, final boolean isConstant) throws IOException {
|
||||
super(updater, RrdPrimitive.RRD_LONG, isConstant);
|
||||
}
|
||||
|
||||
RrdLong(final RrdUpdater updater) throws IOException {
|
||||
this(updater, false);
|
||||
}
|
||||
|
||||
void set(final long value) throws IOException {
|
||||
if (!isCachingAllowed()) {
|
||||
writeLong(value);
|
||||
}
|
||||
// caching allowed
|
||||
else if (!cached || cache != value) {
|
||||
// update cache
|
||||
writeLong(cache = value);
|
||||
cached = true;
|
||||
}
|
||||
}
|
||||
|
||||
long get() throws IOException {
|
||||
return cached ? cache : readLong();
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* Backend to be used to store all RRD bytes in memory.<p>
|
||||
*/
|
||||
public class RrdMemoryBackend extends RrdBackend {
|
||||
private static final ReadWriteLock m_readWritelock = new ReentrantReadWriteLock();
|
||||
private static final Lock m_readLock = m_readWritelock.readLock();
|
||||
private static final Lock m_writeLock = m_readWritelock.writeLock();
|
||||
|
||||
private byte[] buffer = new byte[0];
|
||||
|
||||
protected RrdMemoryBackend(String path) {
|
||||
super(path);
|
||||
}
|
||||
|
||||
protected void write(final long offset, final byte[] b) {
|
||||
m_writeLock.lock();
|
||||
try {
|
||||
int pos = (int) offset;
|
||||
for (final byte singleByte : b) {
|
||||
buffer[pos++] = singleByte;
|
||||
}
|
||||
} finally {
|
||||
m_writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected void read(final long offset, final byte[] b) throws IOException {
|
||||
m_readLock.lock();
|
||||
try {
|
||||
int pos = (int) offset;
|
||||
if (pos + b.length <= buffer.length) {
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
b[i] = buffer[pos++];
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IOException("Not enough bytes available in memory " + getPath());
|
||||
}
|
||||
} finally {
|
||||
m_readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of RRD bytes held in memory.
|
||||
*
|
||||
* @return Number of all RRD bytes.
|
||||
*/
|
||||
public long getLength() {
|
||||
m_readLock.lock();
|
||||
try {
|
||||
return buffer.length;
|
||||
} finally {
|
||||
m_readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserves a memory section as a RRD storage.
|
||||
*
|
||||
* @param newLength Number of bytes held in memory.
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected void setLength(final long newLength) throws IOException {
|
||||
m_writeLock.lock();
|
||||
try {
|
||||
if (newLength > Integer.MAX_VALUE) {
|
||||
throw new IOException("Cannot create this big memory backed RRD");
|
||||
}
|
||||
buffer = new byte[(int) newLength];
|
||||
} finally {
|
||||
m_writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required by the base class definition, but it does not
|
||||
* releases any memory resources at all.
|
||||
*/
|
||||
public void close() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is overridden to disable high-level caching in frontend JRobin classes.
|
||||
*
|
||||
* @return Always returns <code>false</code>. There is no need to cache anything in high-level classes
|
||||
* since all RRD bytes are already in memory.
|
||||
*/
|
||||
protected boolean isCachingAllowed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Factory class which creates actual {@link RrdMemoryBackend} objects. JRobin's support
|
||||
* for in-memory RRDs is still experimental. You should know that all active RrdMemoryBackend
|
||||
* objects are held in memory, each backend object stores RRD data in one big byte array. This
|
||||
* implementation is therefore quite basic and memory hungry but runs very fast.
|
||||
* <p>
|
||||
* Calling {@link RrdDb#close() close()} on RrdDb objects does not release any memory at all
|
||||
* (RRD data must be available for the next <code>new RrdDb(path)</code> call. To release allocated
|
||||
* memory, you'll have to call {@link #delete(String) delete(path)} method of this class.
|
||||
*/
|
||||
public class RrdMemoryBackendFactory extends RrdBackendFactory {
|
||||
/**
|
||||
* factory name, "MEMORY"
|
||||
*/
|
||||
public static final String NAME = "MEMORY";
|
||||
private HashMap<String, RrdMemoryBackend> backends = new HashMap<String, RrdMemoryBackend>();
|
||||
|
||||
/**
|
||||
* Creates RrdMemoryBackend object.
|
||||
*
|
||||
* @param id Since this backend holds all data in memory, this argument is interpreted
|
||||
* as an ID for this memory-based storage.
|
||||
* @param readOnly This parameter is ignored
|
||||
* @return RrdMemoryBackend object which handles all I/O operations
|
||||
*/
|
||||
protected synchronized RrdBackend open(String id, boolean readOnly) {
|
||||
RrdMemoryBackend backend;
|
||||
if (backends.containsKey(id)) {
|
||||
backend = backends.get(id);
|
||||
}
|
||||
else {
|
||||
backend = new RrdMemoryBackend(id);
|
||||
backends.put(id, backend);
|
||||
}
|
||||
return backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if a memory storage with the given ID already exists.
|
||||
*
|
||||
* @param id Memory storage ID.
|
||||
* @return True, if such storage exists, false otherwise.
|
||||
*/
|
||||
protected synchronized boolean exists(String id) {
|
||||
return backends.containsKey(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the storage with the given ID from the memory.
|
||||
*
|
||||
* @param id Storage ID
|
||||
* @return True, if the storage with the given ID is deleted, false otherwise.
|
||||
*/
|
||||
public boolean delete(String id) {
|
||||
if (backends.containsKey(id)) {
|
||||
backends.remove(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this factory.
|
||||
*
|
||||
* @return Factory name (equals to "MEMORY").
|
||||
*/
|
||||
public String getFactoryName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
import engine.misc.DeallocationHelper;
|
||||
|
||||
/**
|
||||
* JRobin backend which is used to store RRD data to ordinary disk files by
|
||||
* using fast java.nio.* package. This is the default backend engine since
|
||||
* JRobin 1.4.0.
|
||||
*/
|
||||
@SuppressWarnings("restriction")
|
||||
public class RrdNioBackend extends RrdFileBackend {
|
||||
private final SyncManager m_syncManager;
|
||||
private MappedByteBuffer m_byteBuffer = null;
|
||||
// Too many ominous warnings from Java 9
|
||||
private static final DeallocationHelper _dHelper = SystemVersion.isJava9() ? null : new DeallocationHelper();
|
||||
|
||||
/**
|
||||
* Creates RrdFileBackend object for the given file path, backed by
|
||||
* java.nio.* classes. This constructor will create a
|
||||
* {@link SyncManager} for each instance, which is very inefficient.
|
||||
* It is recommended that you instead use the
|
||||
* {@link #RrdNioBackend(String, boolean, SyncManager)}
|
||||
* constructor instead.
|
||||
*
|
||||
* @param path
|
||||
* Path to a JRB file.
|
||||
* @param readOnly
|
||||
* True, if file should be open in a read-only mode. False
|
||||
* otherwise
|
||||
* @param syncPeriod
|
||||
* How often (in seconds) to sync MMAP'd RRD data to disk
|
||||
* @throws IOException
|
||||
* Thrown in case of I/O error
|
||||
*/
|
||||
protected RrdNioBackend(final String path, final boolean readOnly, final int syncPeriod) throws IOException {
|
||||
this(path, readOnly, new SyncManager(syncPeriod));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates RrdFileBackend object for the given file path, backed by
|
||||
* java.nio.* classes.
|
||||
*
|
||||
* @param path
|
||||
* Path to a file
|
||||
* @param readOnly
|
||||
* True, if file should be open in a read-only mode. False
|
||||
* otherwise.
|
||||
* @param syncManager
|
||||
* An object for managing synchronization of NIO-backed RRDs,
|
||||
* generally owned by the backend factory. If null, MMAP'd
|
||||
* data will only be synchronized to disk upon unmap. Note
|
||||
* that if the file is opened read-only, the SyncManager is
|
||||
* ignored. {@link RrdNioBackend#unmapFile() unmapFile()}
|
||||
* @throws IOException
|
||||
* Thrown in case of I/O error
|
||||
*/
|
||||
protected RrdNioBackend(final String path, final boolean readOnly, final SyncManager syncManager) throws IOException {
|
||||
super(path, readOnly);
|
||||
m_syncManager = syncManager;
|
||||
|
||||
try {
|
||||
mapFile();
|
||||
} catch (final IOException ioe) {
|
||||
stopSchedule();
|
||||
super.close();
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
private void mapFile() throws IOException {
|
||||
if (!isReadOnly()) {
|
||||
startSchedule();
|
||||
}
|
||||
final long length = getLength();
|
||||
if (length > 0) {
|
||||
final FileChannel.MapMode mapMode = isReadOnly() ? FileChannel.MapMode.READ_ONLY : FileChannel.MapMode.READ_WRITE;
|
||||
m_byteBuffer = file.getChannel().map(mapMode, 0, length);
|
||||
}
|
||||
}
|
||||
|
||||
private void unmapFile() {
|
||||
if (!isReadOnly()) {
|
||||
stopSchedule();
|
||||
}
|
||||
if (_dHelper != null && m_byteBuffer != null) {
|
||||
_dHelper.deallocate(m_byteBuffer);
|
||||
m_byteBuffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void startSchedule() {
|
||||
if (m_syncManager != null) {
|
||||
m_syncManager.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void stopSchedule() {
|
||||
if (m_syncManager != null) {
|
||||
m_syncManager.remove(this);
|
||||
}
|
||||
sync();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
stopSchedule();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets length of the underlying RRD file. This method is called only
|
||||
* once, immediately after a new RRD file gets created.
|
||||
*
|
||||
* @param newLength
|
||||
* Length of the RRD file
|
||||
* @throws IOException
|
||||
* Thrown in case of I/O error.
|
||||
*/
|
||||
protected synchronized void setLength(final long newLength) throws IOException {
|
||||
unmapFile();
|
||||
super.setLength(newLength);
|
||||
mapFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes bytes to the underlying RRD file on the disk
|
||||
*
|
||||
* @param offset
|
||||
* Starting file offset
|
||||
* @param b
|
||||
* Bytes to be written.
|
||||
*/
|
||||
protected synchronized void write(final long offset, final byte[] b) throws IOException {
|
||||
if (m_byteBuffer != null) {
|
||||
m_byteBuffer.position((int) offset);
|
||||
m_byteBuffer.put(b);
|
||||
} else {
|
||||
throw new IOException("Write failed, file " + getPath() + " not mapped for I/O");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a number of bytes from the RRD file on the disk
|
||||
*
|
||||
* @param offset
|
||||
* Starting file offset
|
||||
* @param b
|
||||
* Buffer which receives bytes read from the file.
|
||||
*/
|
||||
protected synchronized void read(final long offset, final byte[] b) throws IOException {
|
||||
if (m_byteBuffer != null) {
|
||||
m_byteBuffer.position((int) offset);
|
||||
m_byteBuffer.get(b);
|
||||
} else {
|
||||
throw new IOException("Read failed, file " + getPath() + " not mapped for I/O");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying RRD file.
|
||||
*
|
||||
* @throws IOException
|
||||
* Thrown in case of I/O error
|
||||
*/
|
||||
public synchronized void close() throws IOException {
|
||||
// cancel synchronization
|
||||
try {
|
||||
unmapFile();
|
||||
} finally {
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method forces all data cached in memory but not yet stored in the
|
||||
* file, to be stored in it.
|
||||
*/
|
||||
protected synchronized void sync() {
|
||||
if (m_byteBuffer != null) {
|
||||
m_byteBuffer.force();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Factory class which creates actual {@link RrdNioBackend} objects. This is
|
||||
* the default factory since 1.4.0 version
|
||||
*/
|
||||
public class RrdNioBackendFactory extends RrdFileBackendFactory {
|
||||
/**
|
||||
* Period in seconds between consecutive synchronizations when sync-mode
|
||||
* is set to SYNC_BACKGROUND. By default in-memory cache will be
|
||||
* transferred to the disc every 300 seconds (5 minutes). Default value
|
||||
* can be changed via {@link #setSyncPeriod(int)} method.
|
||||
*/
|
||||
public static final int DEFAULT_SYNC_PERIOD = 300; // seconds
|
||||
|
||||
private static SyncManager s_syncManager = new SyncManager(DEFAULT_SYNC_PERIOD);
|
||||
|
||||
/**
|
||||
* factory name, "NIO"
|
||||
*/
|
||||
public static final String NAME = "NIO";
|
||||
|
||||
/**
|
||||
* Returns time between two consecutive background synchronizations. If
|
||||
* not changed via {@link #setSyncPeriod(int)} method call, defaults to
|
||||
* {@link #DEFAULT_SYNC_PERIOD}. See {@link #setSyncPeriod(int)} for more
|
||||
* information.
|
||||
*
|
||||
* @return Time in seconds between consecutive background
|
||||
* synchronizations.
|
||||
*/
|
||||
public static int getSyncPeriod() {
|
||||
return s_syncManager.getSyncPeriod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets time between consecutive background synchronizations.
|
||||
*
|
||||
* @param syncPeriod
|
||||
* Time in seconds between consecutive background
|
||||
* synchronizations.
|
||||
*/
|
||||
public synchronized static void setSyncPeriod(final int syncPeriod) {
|
||||
s_syncManager.setSyncPeriod(syncPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates RrdNioBackend object for the given file path.
|
||||
*
|
||||
* @param path
|
||||
* File path
|
||||
* @param readOnly
|
||||
* True, if the file should be accessed in read/only mode.
|
||||
* False otherwise.
|
||||
* @return RrdNioBackend object which handles all I/O operations for the
|
||||
* given file path
|
||||
* @throws IOException
|
||||
* Thrown in case of I/O error.
|
||||
*/
|
||||
protected RrdBackend open(final String path, final boolean readOnly) throws IOException {
|
||||
return new RrdNioBackend(path, readOnly, s_syncManager);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
s_syncManager.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this factory.
|
||||
*
|
||||
* @return Factory name (equals to string "NIO")
|
||||
*/
|
||||
public String getFactoryName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
shutdown();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
SyncManager getSyncManager() {
|
||||
return s_syncManager;
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* JRobin backend which is used to store RRD data to ordinary disk files
|
||||
* by using fast java.nio.* package. This is the default backend engine since JRobin 1.4.0.
|
||||
*/
|
||||
public class RrdNioByteBufferBackend extends RrdFileBackend {
|
||||
|
||||
private ByteBuffer m_byteBuffer;
|
||||
|
||||
private FileChannel m_ch;
|
||||
|
||||
private static final ReadWriteLock m_readWritelock = new ReentrantReadWriteLock();
|
||||
private static final Lock m_readLock = m_readWritelock.readLock();
|
||||
private static final Lock m_writeLock = m_readWritelock.writeLock();
|
||||
|
||||
/**
|
||||
* Creates RrdFileBackend object for the given file path, backed by java.nio.* classes.
|
||||
*
|
||||
* @param path Path to a file
|
||||
* @param readOnly True, if file should be open in a read-only mode. False otherwise
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
protected RrdNioByteBufferBackend(final String path, final boolean readOnly) throws IOException, IllegalStateException {
|
||||
super(path, readOnly);
|
||||
|
||||
if (file != null) {
|
||||
m_ch = file.getChannel();
|
||||
m_byteBuffer = ByteBuffer.allocate((int) m_ch.size());
|
||||
m_ch.read(m_byteBuffer, 0);
|
||||
} else {
|
||||
throw new IllegalStateException("File in base class is null.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets length of the underlying RRD file. This method is called only once, immediately
|
||||
* after a new RRD file gets created.
|
||||
*
|
||||
* @param newLength Length of the RRD file
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
@Override
|
||||
protected void setLength(final long newLength) throws IOException {
|
||||
m_writeLock.lock();
|
||||
try {
|
||||
super.setLength(newLength);
|
||||
m_ch = file.getChannel();
|
||||
m_byteBuffer = ByteBuffer.allocate((int) newLength);
|
||||
m_ch.read(m_byteBuffer, 0);
|
||||
m_byteBuffer.position(0);
|
||||
} finally {
|
||||
m_writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes bytes to the underlying RRD file on the disk
|
||||
*
|
||||
* @param offset Starting file offset
|
||||
* @param b Bytes to be written.
|
||||
*/
|
||||
@Override
|
||||
protected void write(final long offset, final byte[] b) {
|
||||
m_writeLock.lock();
|
||||
try {
|
||||
m_byteBuffer.position((int) offset);
|
||||
m_byteBuffer.put(b);
|
||||
} finally {
|
||||
m_writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a number of bytes from the RRD file on the disk
|
||||
*
|
||||
* @param offset Starting file offset
|
||||
* @param b Buffer which receives bytes read from the file.
|
||||
*/
|
||||
@Override
|
||||
protected void read(final long offset, final byte[] b) {
|
||||
m_readLock.lock();
|
||||
try {
|
||||
m_byteBuffer.position((int) offset);
|
||||
m_byteBuffer.get(b);
|
||||
} finally {
|
||||
m_readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying RRD file.
|
||||
*
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
m_writeLock.lock();
|
||||
try {
|
||||
m_byteBuffer.position(0);
|
||||
|
||||
if (!isReadOnly()) m_ch.write(m_byteBuffer, 0);
|
||||
//just calling close here because the super calls close
|
||||
//on the File object and Java calls close on the channel
|
||||
super.close();
|
||||
} finally {
|
||||
m_writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Factory class which creates actual {@link RrdNioBackend} objects.
|
||||
*/
|
||||
public class RrdNioByteBufferBackendFactory extends RrdFileBackendFactory {
|
||||
|
||||
public static final String NAME = "MNIO";
|
||||
|
||||
/**
|
||||
* Creates RrdNioByteBufferBackend object for the given file path.
|
||||
*
|
||||
* @param path File path
|
||||
* @param readOnly True, if the file should be accessed in read/only mode.
|
||||
* False otherwise.
|
||||
* @return RrdNioBackend object which handles all I/O operations for the given file path
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
@Override
|
||||
protected RrdBackend open(String path, boolean readOnly) throws IOException {
|
||||
return new RrdNioByteBufferBackend(path, readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this factory.
|
||||
*
|
||||
* @return Factory name (equals to string "NIOBB")
|
||||
*/
|
||||
@Override
|
||||
public String getFactoryName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
abstract class RrdPrimitive {
|
||||
static final int STRING_LENGTH = 20;
|
||||
static final int RRD_INT = 0, RRD_LONG = 1, RRD_DOUBLE = 2, RRD_STRING = 3;
|
||||
static final int[] RRD_PRIM_SIZES = {4, 8, 8, 2 * STRING_LENGTH};
|
||||
|
||||
private RrdBackend backend;
|
||||
private int byteCount;
|
||||
private final long pointer;
|
||||
private final boolean cachingAllowed;
|
||||
|
||||
RrdPrimitive(final RrdUpdater updater, final int type, final boolean isConstant) throws IOException {
|
||||
this(updater, type, 1, isConstant);
|
||||
}
|
||||
|
||||
RrdPrimitive(final RrdUpdater updater, final int type, final int count, final boolean isConstant) throws IOException {
|
||||
this.backend = updater.getRrdBackend();
|
||||
this.byteCount = RRD_PRIM_SIZES[type] * count;
|
||||
this.pointer = updater.getRrdAllocator().allocate(byteCount);
|
||||
this.cachingAllowed = isConstant || backend.isCachingAllowed();
|
||||
}
|
||||
|
||||
final byte[] readBytes() throws IOException {
|
||||
final byte[] b = new byte[byteCount];
|
||||
backend.read(pointer, b);
|
||||
return b;
|
||||
}
|
||||
|
||||
final void writeBytes(final byte[] b) throws IOException {
|
||||
assert b.length == byteCount: "Invalid number of bytes supplied to RrdPrimitive.write method";
|
||||
backend.write(pointer, b);
|
||||
}
|
||||
|
||||
final int readInt() throws IOException {
|
||||
return backend.readInt(pointer);
|
||||
}
|
||||
|
||||
final void writeInt(final int value) throws IOException {
|
||||
backend.writeInt(pointer, value);
|
||||
}
|
||||
|
||||
final long readLong() throws IOException {
|
||||
return backend.readLong(pointer);
|
||||
}
|
||||
|
||||
final void writeLong(final long value) throws IOException {
|
||||
backend.writeLong(pointer, value);
|
||||
}
|
||||
|
||||
final double readDouble() throws IOException {
|
||||
return backend.readDouble(pointer);
|
||||
}
|
||||
|
||||
final double readDouble(final int index) throws IOException {
|
||||
final long offset = pointer + ((long)index * (long)RRD_PRIM_SIZES[RRD_DOUBLE]);
|
||||
return backend.readDouble(offset);
|
||||
}
|
||||
|
||||
final double[] readDouble(final int index, final int count) throws IOException {
|
||||
final long offset = pointer + ((long)index * (long)RRD_PRIM_SIZES[RRD_DOUBLE]);
|
||||
return backend.readDouble(offset, count);
|
||||
}
|
||||
|
||||
final void writeDouble(final double value) throws IOException {
|
||||
backend.writeDouble(pointer, value);
|
||||
}
|
||||
|
||||
final void writeDouble(final int index, final double value, final int count) throws IOException {
|
||||
final long offset = pointer + ((long)index * (long)RRD_PRIM_SIZES[RRD_DOUBLE]);
|
||||
backend.writeDouble(offset, value, count);
|
||||
}
|
||||
|
||||
final void writeDouble(final int index, final double[] values) throws IOException {
|
||||
final long offset = pointer + ((long)index * (long)RRD_PRIM_SIZES[RRD_DOUBLE]);
|
||||
backend.writeDouble(offset, values);
|
||||
}
|
||||
|
||||
final String readString() throws IOException {
|
||||
return backend.readString(pointer);
|
||||
}
|
||||
|
||||
final void writeString(final String value) throws IOException {
|
||||
backend.writeString(pointer, value);
|
||||
}
|
||||
|
||||
final boolean isCachingAllowed() {
|
||||
return cachingAllowed;
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
/**
|
||||
* JRobin backend which is used to store RRD data to ordinary files on the disk. This backend
|
||||
* is SAFE: it locks the underlying RRD file during update/fetch operations, and caches only static
|
||||
* parts of a RRD file in memory. Therefore, this backend is safe to be used when RRD files should
|
||||
* be shared between several JVMs at the same time. However, this backend is a little bit slow
|
||||
* since it does not use fast java.nio.* package (it's still based on the RandomAccessFile class).
|
||||
*/
|
||||
public class RrdSafeFileBackend extends RrdFileBackend {
|
||||
private static final Counters counters = new Counters();
|
||||
|
||||
private FileLock m_lock;
|
||||
|
||||
/**
|
||||
* Creates RrdFileBackend object for the given file path, backed by RandomAccessFile object.
|
||||
*
|
||||
* @param path Path to a file
|
||||
* @param lockWaitTime lock waiting time in milliseconds
|
||||
* @param lockRetryPeriod lock retry period in milliseconds
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public RrdSafeFileBackend(final String path, final long lockWaitTime, final long lockRetryPeriod) throws IOException {
|
||||
super(path, false);
|
||||
try {
|
||||
lockFile(lockWaitTime, lockRetryPeriod);
|
||||
}
|
||||
catch (final IOException ioe) {
|
||||
super.close();
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
private void lockFile(final long lockWaitTime, final long lockRetryPeriod) throws IOException {
|
||||
final long entryTime = System.currentTimeMillis();
|
||||
final FileChannel channel = file.getChannel();
|
||||
m_lock = channel.tryLock(0, Long.MAX_VALUE, false);
|
||||
if (m_lock != null) {
|
||||
counters.registerQuickLock();
|
||||
return;
|
||||
}
|
||||
do {
|
||||
try {
|
||||
Thread.sleep(lockRetryPeriod);
|
||||
}
|
||||
catch (final InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
m_lock = channel.tryLock(0, Long.MAX_VALUE, false);
|
||||
if (m_lock != null) {
|
||||
counters.registerDelayedLock();
|
||||
return;
|
||||
}
|
||||
} while (System.currentTimeMillis() - entryTime <= lockWaitTime);
|
||||
counters.registerError();
|
||||
throw new IOException("Could not obtain exclusive m_lock on file: " + getPath() + "] after " + lockWaitTime + " milliseconds");
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
if (m_lock != null) {
|
||||
m_lock.release();
|
||||
m_lock = null;
|
||||
counters.registerUnlock();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the caching policy for this backend.
|
||||
*
|
||||
* @return <code>false</code>
|
||||
*/
|
||||
protected boolean isCachingAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getLockInfo() {
|
||||
return counters.getInfo();
|
||||
}
|
||||
|
||||
static class Counters {
|
||||
long locks, quickLocks, unlocks, locked, errors;
|
||||
|
||||
synchronized void registerQuickLock() {
|
||||
locks++;
|
||||
quickLocks++;
|
||||
locked++;
|
||||
}
|
||||
|
||||
synchronized void registerDelayedLock() {
|
||||
locks++;
|
||||
locked++;
|
||||
}
|
||||
|
||||
synchronized void registerUnlock() {
|
||||
unlocks++;
|
||||
locked--;
|
||||
}
|
||||
|
||||
synchronized void registerError() {
|
||||
errors++;
|
||||
}
|
||||
|
||||
synchronized String getInfo() {
|
||||
return "LOCKS=" + locks + ", " + "UNLOCKS=" + unlocks + ", " +
|
||||
"DELAYED_LOCKS=" + (locks - quickLocks) + ", " + "LOCKED=" + locked + ", " +
|
||||
"ERRORS=" + errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Factory class which creates actual {@link RrdSafeFileBackend} objects.
|
||||
*/
|
||||
public class RrdSafeFileBackendFactory extends RrdFileBackendFactory {
|
||||
/**
|
||||
* Default time (in milliseconds) this backend will wait for a file lock.
|
||||
*/
|
||||
public static final long LOCK_WAIT_TIME = 3000L;
|
||||
private static long lockWaitTime = LOCK_WAIT_TIME;
|
||||
|
||||
/**
|
||||
* Default time between two consecutive file locking attempts.
|
||||
*/
|
||||
public static final long LOCK_RETRY_PERIOD = 50L;
|
||||
private static long lockRetryPeriod = LOCK_RETRY_PERIOD;
|
||||
|
||||
/**
|
||||
* factory name, "SAFE"
|
||||
*/
|
||||
public static final String NAME = "SAFE";
|
||||
|
||||
/**
|
||||
* Creates RrdSafeFileBackend object for the given file path.
|
||||
*
|
||||
* @param path File path
|
||||
* @param readOnly This parameter is ignored
|
||||
* @return RrdSafeFileBackend object which handles all I/O operations for the given file path
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
*/
|
||||
protected RrdBackend open(String path, boolean readOnly) throws IOException {
|
||||
return new RrdSafeFileBackend(path, lockWaitTime, lockRetryPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this factory.
|
||||
*
|
||||
* @return Factory name (equals to string "SAFE")
|
||||
*/
|
||||
public String getFactoryName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns time this backend will wait for a file lock.
|
||||
*
|
||||
* @return Time (in milliseconds) this backend will wait for a file lock.
|
||||
*/
|
||||
public static long getLockWaitTime() {
|
||||
return lockWaitTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets time this backend will wait for a file lock.
|
||||
*
|
||||
* @param lockWaitTime Maximum lock wait time (in milliseconds)
|
||||
*/
|
||||
public static void setLockWaitTime(long lockWaitTime) {
|
||||
RrdSafeFileBackendFactory.lockWaitTime = lockWaitTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns time between two consecutive file locking attempts.
|
||||
*
|
||||
* @return Time (im milliseconds) between two consecutive file locking attempts.
|
||||
*/
|
||||
public static long getLockRetryPeriod() {
|
||||
return lockRetryPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets time between two consecutive file locking attempts.
|
||||
*
|
||||
* @param lockRetryPeriod time (in milliseconds) between two consecutive file locking attempts.
|
||||
*/
|
||||
public static void setLockRetryPeriod(long lockRetryPeriod) {
|
||||
RrdSafeFileBackendFactory.lockRetryPeriod = lockRetryPeriod;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class RrdString extends RrdPrimitive {
|
||||
private String cache;
|
||||
|
||||
RrdString(final RrdUpdater updater, final boolean isConstant) throws IOException {
|
||||
super(updater, RrdPrimitive.RRD_STRING, isConstant);
|
||||
}
|
||||
|
||||
RrdString(final RrdUpdater updater) throws IOException {
|
||||
this(updater, false);
|
||||
}
|
||||
|
||||
void set(final String value) throws IOException {
|
||||
if (!isCachingAllowed()) {
|
||||
writeString(value);
|
||||
}
|
||||
// caching allowed
|
||||
else if (cache == null || !cache.equals(value)) {
|
||||
// update cache
|
||||
writeString(cache = value);
|
||||
}
|
||||
}
|
||||
|
||||
String get() throws IOException {
|
||||
return (cache != null) ? cache : readString();
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jrobin.core.jrrd.RRDatabase;
|
||||
|
||||
class RrdToolReader extends DataImporter {
|
||||
private RRDatabase rrd;
|
||||
|
||||
RrdToolReader(String rrdPath) throws IOException,RrdException {
|
||||
rrd = new RRDatabase(rrdPath);
|
||||
}
|
||||
|
||||
String getVersion() {
|
||||
return rrd.getHeader().getVersion();
|
||||
}
|
||||
|
||||
long getLastUpdateTime() {
|
||||
return Util.getTimestamp(rrd.getLastUpdate());
|
||||
}
|
||||
|
||||
long getStep() {
|
||||
return rrd.getHeader().getPDPStep();
|
||||
}
|
||||
|
||||
int getDsCount() {
|
||||
return rrd.getHeader().getDSCount();
|
||||
}
|
||||
|
||||
int getArcCount() throws RrdException, IOException {
|
||||
return rrd.getNumArchives();
|
||||
}
|
||||
|
||||
String getDsName(int dsIndex) {
|
||||
return rrd.getDataSource(dsIndex).getName();
|
||||
}
|
||||
|
||||
String getDsType(int dsIndex) {
|
||||
return rrd.getDataSource(dsIndex).getType().toString();
|
||||
}
|
||||
|
||||
long getHeartbeat(int dsIndex) {
|
||||
return rrd.getDataSource(dsIndex).getMinimumHeartbeat();
|
||||
}
|
||||
|
||||
double getMinValue(int dsIndex) {
|
||||
return rrd.getDataSource(dsIndex).getMinimum();
|
||||
}
|
||||
|
||||
double getMaxValue(int dsIndex) {
|
||||
return rrd.getDataSource(dsIndex).getMaximum();
|
||||
}
|
||||
|
||||
double getLastValue(int dsIndex) {
|
||||
String valueStr = rrd.getDataSource(dsIndex).getPDPStatusBlock().getLastReading();
|
||||
return Util.parseDouble(valueStr);
|
||||
}
|
||||
|
||||
double getAccumValue(int dsIndex) {
|
||||
return rrd.getDataSource(dsIndex).getPDPStatusBlock().getValue();
|
||||
}
|
||||
|
||||
long getNanSeconds(int dsIndex) {
|
||||
return rrd.getDataSource(dsIndex).getPDPStatusBlock().getUnknownSeconds();
|
||||
}
|
||||
|
||||
String getConsolFun(int arcIndex) {
|
||||
return rrd.getArchive(arcIndex).getType().toString();
|
||||
}
|
||||
|
||||
double getXff(int arcIndex) {
|
||||
return rrd.getArchive(arcIndex).getXff();
|
||||
}
|
||||
|
||||
int getSteps(int arcIndex) {
|
||||
return rrd.getArchive(arcIndex).getPdpCount();
|
||||
}
|
||||
|
||||
int getRows(int arcIndex) throws RrdException, IOException {
|
||||
return rrd.getArchive(arcIndex).getRowCount();
|
||||
}
|
||||
|
||||
double getStateAccumValue(int arcIndex, int dsIndex) throws RrdException, IOException {
|
||||
return rrd.getArchive(arcIndex).getCDPStatusBlock(dsIndex).getValue();
|
||||
}
|
||||
|
||||
int getStateNanSteps(int arcIndex, int dsIndex) throws RrdException, IOException {
|
||||
return rrd.getArchive(arcIndex).getCDPStatusBlock(dsIndex).getUnknownDatapoints();
|
||||
}
|
||||
|
||||
double[] getValues(int arcIndex, int dsIndex) throws RrdException, IOException,RrdException {
|
||||
return rrd.getArchive(arcIndex).getValues()[dsIndex];
|
||||
}
|
||||
|
||||
void release() throws IOException {
|
||||
if (rrd != null) {
|
||||
rrd.close();
|
||||
rrd = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
release();
|
||||
}
|
||||
}
|
||||
@@ -1,658 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Class used to perform various complex operations on RRD files. Use an instance of the
|
||||
* RrdToolkit class to:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>add datasource to a RRD file.
|
||||
* <li>add archive to a RRD file.
|
||||
* <li>remove datasource from a RRD file.
|
||||
* <li>remove archive from a RRD file.
|
||||
* </ul>
|
||||
* All these operations can be performed on the copy of the original RRD file, or on the
|
||||
* original file itself (with possible backup file creation).
|
||||
* <p>
|
||||
* <b><u>IMPORTANT</u></b>: NEVER use methods found in this class on 'live' RRD files
|
||||
* (files which are currently in use).
|
||||
*/
|
||||
public class RrdToolkit {
|
||||
/**
|
||||
* Creates a new RRD file with one more datasource in it. RRD file is created based on the
|
||||
* existing one (the original RRD file is not modified at all). All data from
|
||||
* the original RRD file is copied to the new one.
|
||||
*
|
||||
* @param sourcePath path to a RRD file to import data from (will not be modified)
|
||||
* @param destPath path to a new RRD file (will be created)
|
||||
* @param newDatasource Datasource definition to be added to the new RRD file
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void addDatasource(String sourcePath, String destPath, DsDef newDatasource)
|
||||
throws IOException, RrdException {
|
||||
if (Util.sameFilePath(sourcePath, destPath)) {
|
||||
throw new RrdException("Source and destination paths are the same");
|
||||
}
|
||||
RrdDb rrdSource = new RrdDb(sourcePath);
|
||||
try {
|
||||
RrdDef rrdDef = rrdSource.getRrdDef();
|
||||
rrdDef.setPath(destPath);
|
||||
rrdDef.addDatasource(newDatasource);
|
||||
RrdDb rrdDest = new RrdDb(rrdDef);
|
||||
try {
|
||||
rrdSource.copyStateTo(rrdDest);
|
||||
}
|
||||
finally {
|
||||
rrdDest.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rrdSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds one more datasource to a RRD file.</p>
|
||||
* <p>WARNING: This method is potentialy dangerous! It will modify your RRD file.
|
||||
* It is highly recommended to preserve the original RRD file (<i>saveBackup</i>
|
||||
* should be set to <code>true</code>). The backup file will be created in the same
|
||||
* directory as the original one with <code>.bak</code> extension added to the
|
||||
* original name.</p>
|
||||
* <p>Before applying this method, be sure that the specified RRD file is not in use
|
||||
* (not open)</p>
|
||||
*
|
||||
* @param sourcePath path to a RRD file to add datasource to.
|
||||
* @param newDatasource Datasource definition to be added to the RRD file
|
||||
* @param saveBackup true, if backup of the original file should be created;
|
||||
* false, otherwise
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void addDatasource(String sourcePath, DsDef newDatasource, boolean saveBackup)
|
||||
throws IOException, RrdException {
|
||||
String destPath = Util.getTmpFilename();
|
||||
addDatasource(sourcePath, destPath, newDatasource);
|
||||
copyFile(destPath, sourcePath, saveBackup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new RRD file with one datasource removed. RRD file is created based on the
|
||||
* existing one (the original RRD file is not modified at all). All remaining data from
|
||||
* the original RRD file is copied to the new one.
|
||||
*
|
||||
* @param sourcePath path to a RRD file to import data from (will not be modified)
|
||||
* @param destPath path to a new RRD file (will be created)
|
||||
* @param dsName Name of the Datasource to be removed from the new RRD file
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void removeDatasource(String sourcePath, String destPath, String dsName)
|
||||
throws IOException, RrdException {
|
||||
if (Util.sameFilePath(sourcePath, destPath)) {
|
||||
throw new RrdException("Source and destination paths are the same");
|
||||
}
|
||||
RrdDb rrdSource = new RrdDb(sourcePath);
|
||||
try {
|
||||
RrdDef rrdDef = rrdSource.getRrdDef();
|
||||
rrdDef.setPath(destPath);
|
||||
rrdDef.removeDatasource(dsName);
|
||||
RrdDb rrdDest = new RrdDb(rrdDef);
|
||||
try {
|
||||
rrdSource.copyStateTo(rrdDest);
|
||||
}
|
||||
finally {
|
||||
rrdDest.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rrdSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Removes single datasource from a RRD file.</p>
|
||||
* <p>WARNING: This method is potentialy dangerous! It will modify your RRD file.
|
||||
* It is highly recommended to preserve the original RRD file (<i>saveBackup</i>
|
||||
* should be set to <code>true</code>). The backup file will be created in the same
|
||||
* directory as the original one with <code>.bak</code> extension added to the
|
||||
* original name.</p>
|
||||
* <p>Before applying this method, be sure that the specified RRD file is not in use
|
||||
* (not open)</p>
|
||||
*
|
||||
* @param sourcePath path to a RRD file to remove datasource from.
|
||||
* @param dsName Name of the Datasource to be removed from the RRD file
|
||||
* @param saveBackup true, if backup of the original file should be created;
|
||||
* false, otherwise
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void removeDatasource(String sourcePath, String dsName, boolean saveBackup)
|
||||
throws IOException, RrdException {
|
||||
String destPath = Util.getTmpFilename();
|
||||
removeDatasource(sourcePath, destPath, dsName);
|
||||
copyFile(destPath, sourcePath, saveBackup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames single datasource in the given RRD file.
|
||||
*
|
||||
* @param sourcePath Path to a RRD file
|
||||
* @param oldDsName Old datasource name
|
||||
* @param newDsName New datasource name
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error (invalid path or datasource names,
|
||||
* for example)
|
||||
*/
|
||||
public static void renameDatasource(String sourcePath, String oldDsName, String newDsName)
|
||||
throws IOException, RrdException {
|
||||
RrdDb rrd = new RrdDb(sourcePath);
|
||||
try {
|
||||
if (rrd.containsDs(oldDsName)) {
|
||||
Datasource datasource = rrd.getDatasource(oldDsName);
|
||||
datasource.setDsName(newDsName);
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Could not find datasource [" + oldDsName + "] in file " + sourcePath);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rrd.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates single or all datasource names in the specified RRD file
|
||||
* by appending '!' (if not already present). Datasources with names ending with '!'
|
||||
* will never store NaNs in RRA archives (zero value will be used instead). Might be useful
|
||||
* from time to time
|
||||
*
|
||||
* @param sourcePath Path to a RRD file
|
||||
* @param dsName Datasource name or null if you want to rename all datasources
|
||||
* @return Number of datasources successfully renamed
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error (invalid path or datasource name,
|
||||
* for example)
|
||||
*/
|
||||
public static int forceZerosForNans(String sourcePath, String dsName) throws IOException, RrdException {
|
||||
RrdDb rrd = new RrdDb(sourcePath);
|
||||
try {
|
||||
Datasource[] datasources;
|
||||
if (dsName == null) {
|
||||
datasources = rrd.getDatasources();
|
||||
}
|
||||
else {
|
||||
if (rrd.containsDs(dsName)) {
|
||||
datasources = new Datasource[] {rrd.getDatasource(dsName)};
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Could not find datasource [" + dsName + "] in file " + sourcePath);
|
||||
}
|
||||
}
|
||||
int count = 0;
|
||||
for (Datasource datasource : datasources) {
|
||||
String currentDsName = datasource.getDsName();
|
||||
if (!currentDsName.endsWith(DsDef.FORCE_ZEROS_FOR_NANS_SUFFIX)) {
|
||||
datasource.setDsName(currentDsName + DsDef.FORCE_ZEROS_FOR_NANS_SUFFIX);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
finally {
|
||||
rrd.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new RRD file with one more archive in it. RRD file is created based on the
|
||||
* existing one (the original RRD file is not modified at all). All data from
|
||||
* the original RRD file is copied to the new one.
|
||||
*
|
||||
* @param sourcePath path to a RRD file to import data from (will not be modified)
|
||||
* @param destPath path to a new RRD file (will be created)
|
||||
* @param newArchive Archive definition to be added to the new RRD file
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void addArchive(String sourcePath, String destPath, ArcDef newArchive)
|
||||
throws IOException, RrdException {
|
||||
if (Util.sameFilePath(sourcePath, destPath)) {
|
||||
throw new RrdException("Source and destination paths are the same");
|
||||
}
|
||||
RrdDb rrdSource = new RrdDb(sourcePath);
|
||||
try {
|
||||
RrdDef rrdDef = rrdSource.getRrdDef();
|
||||
rrdDef.setPath(destPath);
|
||||
rrdDef.addArchive(newArchive);
|
||||
RrdDb rrdDest = new RrdDb(rrdDef);
|
||||
try {
|
||||
rrdSource.copyStateTo(rrdDest);
|
||||
}
|
||||
finally {
|
||||
rrdDest.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rrdSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds one more archive to a RRD file.</p>
|
||||
* <p>WARNING: This method is potentialy dangerous! It will modify your RRD file.
|
||||
* It is highly recommended to preserve the original RRD file (<i>saveBackup</i>
|
||||
* should be set to <code>true</code>). The backup file will be created in the same
|
||||
* directory as the original one with <code>.bak</code> extension added to the
|
||||
* original name.</p>
|
||||
* <p>Before applying this method, be sure that the specified RRD file is not in use
|
||||
* (not open)</p>
|
||||
*
|
||||
* @param sourcePath path to a RRD file to add datasource to.
|
||||
* @param newArchive Archive definition to be added to the RRD file
|
||||
* @param saveBackup true, if backup of the original file should be created;
|
||||
* false, otherwise
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void addArchive(String sourcePath, ArcDef newArchive, boolean saveBackup)
|
||||
throws IOException, RrdException {
|
||||
String destPath = Util.getTmpFilename();
|
||||
addArchive(sourcePath, destPath, newArchive);
|
||||
copyFile(destPath, sourcePath, saveBackup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new RRD file with one archive removed. RRD file is created based on the
|
||||
* existing one (the original RRD file is not modified at all). All relevant data from
|
||||
* the original RRD file is copied to the new one.
|
||||
*
|
||||
* @param sourcePath path to a RRD file to import data from (will not be modified)
|
||||
* @param destPath path to a new RRD file (will be created)
|
||||
* @param consolFun Consolidation function of Archive which should be removed
|
||||
* @param steps Number of steps for Archive which should be removed
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void removeArchive(String sourcePath, String destPath, String consolFun, int steps)
|
||||
throws IOException, RrdException {
|
||||
if (Util.sameFilePath(sourcePath, destPath)) {
|
||||
throw new RrdException("Source and destination paths are the same");
|
||||
}
|
||||
RrdDb rrdSource = new RrdDb(sourcePath);
|
||||
try {
|
||||
RrdDef rrdDef = rrdSource.getRrdDef();
|
||||
rrdDef.setPath(destPath);
|
||||
rrdDef.removeArchive(consolFun, steps);
|
||||
RrdDb rrdDest = new RrdDb(rrdDef);
|
||||
try {
|
||||
rrdSource.copyStateTo(rrdDest);
|
||||
}
|
||||
finally {
|
||||
rrdDest.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rrdSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Removes one archive from a RRD file.</p>
|
||||
* <p>WARNING: This method is potentialy dangerous! It will modify your RRD file.
|
||||
* It is highly recommended to preserve the original RRD file (<i>saveBackup</i>
|
||||
* should be set to <code>true</code>). The backup file will be created in the same
|
||||
* directory as the original one with <code>.bak</code> extension added to the
|
||||
* original name.</p>
|
||||
* <p>Before applying this method, be sure that the specified RRD file is not in use
|
||||
* (not open)</p>
|
||||
*
|
||||
* @param sourcePath path to a RRD file to add datasource to.
|
||||
* @param consolFun Consolidation function of Archive which should be removed
|
||||
* @param steps Number of steps for Archive which should be removed
|
||||
* @param saveBackup true, if backup of the original file should be created;
|
||||
* false, otherwise
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void removeArchive(String sourcePath, String consolFun, int steps,
|
||||
boolean saveBackup) throws IOException, RrdException {
|
||||
String destPath = Util.getTmpFilename();
|
||||
removeArchive(sourcePath, destPath, consolFun, steps);
|
||||
copyFile(destPath, sourcePath, saveBackup);
|
||||
}
|
||||
|
||||
private static void copyFile(String sourcePath, String destPath, boolean saveBackup)
|
||||
throws IOException {
|
||||
File source = new File(sourcePath);
|
||||
File dest = new File(destPath);
|
||||
if (saveBackup) {
|
||||
String backupPath = getBackupPath(destPath);
|
||||
File backup = new File(backupPath);
|
||||
deleteFile(backup);
|
||||
if (!dest.renameTo(backup)) {
|
||||
throw new IOException("Could not create backup file " + backupPath);
|
||||
}
|
||||
}
|
||||
deleteFile(dest);
|
||||
if (!source.renameTo(dest)) {
|
||||
throw new IOException("Could not create file " + destPath + " from " + sourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getBackupPath(String destPath) {
|
||||
StringBuffer sb = new StringBuffer(destPath);
|
||||
do {
|
||||
sb.append(".bak");
|
||||
} while (Util.fileExists(sb.toString()));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets datasource heartbeat to a new value.
|
||||
*
|
||||
* @param sourcePath Path to exisiting RRD file (will be updated)
|
||||
* @param datasourceName Name of the datasource in the specified RRD file
|
||||
* @param newHeartbeat New datasource heartbeat
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public static void setDsHeartbeat(String sourcePath, String datasourceName,
|
||||
long newHeartbeat) throws RrdException, IOException {
|
||||
RrdDb rrd = new RrdDb(sourcePath);
|
||||
try {
|
||||
Datasource ds = rrd.getDatasource(datasourceName);
|
||||
ds.setHeartbeat(newHeartbeat);
|
||||
}
|
||||
finally {
|
||||
rrd.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets datasource heartbeat to a new value.
|
||||
*
|
||||
* @param sourcePath Path to exisiting RRD file (will be updated)
|
||||
* @param dsIndex Index of the datasource in the specified RRD file
|
||||
* @param newHeartbeat New datasource heartbeat
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public static void setDsHeartbeat(String sourcePath, int dsIndex, long newHeartbeat)
|
||||
throws RrdException, IOException {
|
||||
RrdDb rrd = new RrdDb(sourcePath);
|
||||
try {
|
||||
Datasource ds = rrd.getDatasource(dsIndex);
|
||||
ds.setHeartbeat(newHeartbeat);
|
||||
}
|
||||
finally {
|
||||
rrd.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets datasource min value to a new value
|
||||
*
|
||||
* @param sourcePath Path to exisiting RRD file (will be updated)
|
||||
* @param datasourceName Name of the datasource in the specified RRD file
|
||||
* @param newMinValue New min value for the datasource
|
||||
* @param filterArchivedValues set to <code>true</code> if archived values less than
|
||||
* <code>newMinValue</code> should be set to NaN; set to false, otherwise.
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public static void setDsMinValue(String sourcePath, String datasourceName,
|
||||
double newMinValue, boolean filterArchivedValues) throws RrdException, IOException {
|
||||
RrdDb rrd = new RrdDb(sourcePath);
|
||||
try {
|
||||
Datasource ds = rrd.getDatasource(datasourceName);
|
||||
ds.setMinValue(newMinValue, filterArchivedValues);
|
||||
}
|
||||
finally {
|
||||
rrd.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets datasource max value to a new value.
|
||||
*
|
||||
* @param sourcePath Path to exisiting RRD file (will be updated)
|
||||
* @param datasourceName Name of the datasource in the specified RRD file
|
||||
* @param newMaxValue New max value for the datasource
|
||||
* @param filterArchivedValues set to <code>true</code> if archived values greater than
|
||||
* <code>newMaxValue</code> should be set to NaN; set to false, otherwise.
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public static void setDsMaxValue(String sourcePath, String datasourceName,
|
||||
double newMaxValue, boolean filterArchivedValues) throws RrdException, IOException {
|
||||
RrdDb rrd = new RrdDb(sourcePath);
|
||||
try {
|
||||
Datasource ds = rrd.getDatasource(datasourceName);
|
||||
ds.setMaxValue(newMaxValue, filterArchivedValues);
|
||||
}
|
||||
finally {
|
||||
rrd.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates valid value range for the given datasource.
|
||||
*
|
||||
* @param sourcePath Path to exisiting RRD file (will be updated)
|
||||
* @param datasourceName Name of the datasource in the specified RRD file
|
||||
* @param newMinValue New min value for the datasource
|
||||
* @param newMaxValue New max value for the datasource
|
||||
* @param filterArchivedValues set to <code>true</code> if archived values outside
|
||||
* of the specified min/max range should be replaced with NaNs.
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public static void setDsMinMaxValue(String sourcePath, String datasourceName,
|
||||
double newMinValue, double newMaxValue, boolean filterArchivedValues)
|
||||
throws RrdException, IOException {
|
||||
RrdDb rrd = new RrdDb(sourcePath);
|
||||
try {
|
||||
Datasource ds = rrd.getDatasource(datasourceName);
|
||||
ds.setMinMaxValue(newMinValue, newMaxValue, filterArchivedValues);
|
||||
}
|
||||
finally {
|
||||
rrd.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets single archive's X-files factor to a new value.
|
||||
*
|
||||
* @param sourcePath Path to existing RRD file (will be updated)
|
||||
* @param consolFun Consolidation function of the target archive
|
||||
* @param steps Number of sptes of the target archive
|
||||
* @param newXff New X-files factor for the target archive
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public static void setArcXff(String sourcePath, String consolFun, int steps,
|
||||
double newXff) throws RrdException, IOException {
|
||||
RrdDb rrd = new RrdDb(sourcePath);
|
||||
try {
|
||||
Archive arc = rrd.getArchive(consolFun, steps);
|
||||
arc.setXff(newXff);
|
||||
}
|
||||
finally {
|
||||
rrd.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new RRD file based on the existing one, but with a different
|
||||
* size (number of rows) for a single archive. The archive to be resized
|
||||
* is identified by its consolidation function and the number of steps.
|
||||
*
|
||||
* @param sourcePath Path to the source RRD file (will not be modified)
|
||||
* @param destPath Path to the new RRD file (will be created)
|
||||
* @param consolFun Consolidation function of the archive to be resized
|
||||
* @param numSteps Number of steps of the archive to be resized
|
||||
* @param newRows New archive size (number of archive rows)
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void resizeArchive(String sourcePath, String destPath, String consolFun,
|
||||
int numSteps, int newRows)
|
||||
throws IOException, RrdException {
|
||||
if (Util.sameFilePath(sourcePath, destPath)) {
|
||||
throw new RrdException("Source and destination paths are the same");
|
||||
}
|
||||
if (newRows < 2) {
|
||||
throw new RrdException("New arcihve size must be at least 2");
|
||||
}
|
||||
RrdDb rrdSource = new RrdDb(sourcePath);
|
||||
try {
|
||||
RrdDef rrdDef = rrdSource.getRrdDef();
|
||||
ArcDef arcDef = rrdDef.findArchive(consolFun, numSteps);
|
||||
if (arcDef.getRows() != newRows) {
|
||||
arcDef.setRows(newRows);
|
||||
rrdDef.setPath(destPath);
|
||||
RrdDb rrdDest = new RrdDb(rrdDef);
|
||||
try {
|
||||
rrdSource.copyStateTo(rrdDest);
|
||||
}
|
||||
finally {
|
||||
rrdDest.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rrdSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies existing RRD file, by resizing its chosen archive. The archive to be resized
|
||||
* is identified by its consolidation function and the number of steps.
|
||||
*
|
||||
* @param sourcePath Path to the RRD file (will be modified)
|
||||
* @param consolFun Consolidation function of the archive to be resized
|
||||
* @param numSteps Number of steps of the archive to be resized
|
||||
* @param newRows New archive size (number of archive rows)
|
||||
* @param saveBackup true, if backup of the original file should be created;
|
||||
* false, otherwise
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void resizeArchive(String sourcePath, String consolFun,
|
||||
int numSteps, int newRows, boolean saveBackup)
|
||||
throws IOException, RrdException {
|
||||
String destPath = Util.getTmpFilename();
|
||||
resizeArchive(sourcePath, destPath, consolFun, numSteps, newRows);
|
||||
copyFile(destPath, sourcePath, saveBackup);
|
||||
}
|
||||
|
||||
private static void deleteFile(File file) throws IOException {
|
||||
if (file.exists() && !file.delete()) {
|
||||
throw new IOException("Could not delete file: " + file.getCanonicalPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits single RRD file with several datasources into a number of smaller RRD files
|
||||
* with a single datasource in it. All archived values are preserved. If
|
||||
* you have a RRD file named 'traffic.rrd' with two datasources, 'in' and 'out', this
|
||||
* method will create two files (with a single datasource, in the same directory)
|
||||
* named 'in-traffic.rrd' and 'out-traffic.rrd'.
|
||||
*
|
||||
* @param sourcePath Path to a RRD file with multiple datasources defined
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public static void split(String sourcePath) throws IOException, RrdException {
|
||||
RrdDb rrdSource = new RrdDb(sourcePath);
|
||||
try {
|
||||
String[] dsNames = rrdSource.getDsNames();
|
||||
for (String dsName : dsNames) {
|
||||
RrdDef rrdDef = rrdSource.getRrdDef();
|
||||
rrdDef.setPath(createSplitPath(dsName, sourcePath));
|
||||
rrdDef.saveSingleDatasource(dsName);
|
||||
RrdDb rrdDest = new RrdDb(rrdDef);
|
||||
try {
|
||||
rrdSource.copyStateTo(rrdDest);
|
||||
}
|
||||
finally {
|
||||
rrdDest.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rrdSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of canonical file names with the specified extension in the given directory. This
|
||||
* method is not RRD related, but might come handy to create a quick list of all RRD files
|
||||
* in the given directory.
|
||||
*
|
||||
* @param directory Source directory
|
||||
* @param extension File extension (like ".rrd", ".jrb", ".rrd.jrb")
|
||||
* @param resursive true if all subdirectories should be traversed for the same extension, false otherwise
|
||||
* @return Array of sorted canonical file names with the given extension
|
||||
* @throws IOException Thrown in case of I/O error
|
||||
*/
|
||||
public static String[] getCanonicalPaths(String directory, final String extension, boolean resursive)
|
||||
throws IOException {
|
||||
File baseDir = new File(directory);
|
||||
if (!baseDir.isDirectory()) {
|
||||
throw new IOException("Not a directory: " + directory);
|
||||
}
|
||||
List<String> fileList = new LinkedList<String>();
|
||||
traverseDirectory(new File(directory), extension, resursive, fileList);
|
||||
String[] result = fileList.toArray(new String[fileList.size()]);
|
||||
Arrays.sort(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void traverseDirectory(File directory, String extension, boolean recursive, List<String> list)
|
||||
throws IOException {
|
||||
File[] files = directory.listFiles();
|
||||
for (File file : files) {
|
||||
if (file.isDirectory() && recursive) {
|
||||
// traverse subdirectories only if recursive flag is specified
|
||||
traverseDirectory(file, extension, recursive, list);
|
||||
}
|
||||
else if (file.isFile() && file.getName().endsWith(extension)) {
|
||||
list.add(file.getCanonicalPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String createSplitPath(String dsName, String sourcePath) {
|
||||
File file = new File(sourcePath);
|
||||
String newName = dsName + "-" + file.getName();
|
||||
String path = file.getAbsolutePath();
|
||||
String parentDir = path.substring(0, 1 + path.lastIndexOf(Util.getFileSeparator()));
|
||||
return parentDir + newName;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
interface RrdUpdater {
|
||||
public RrdBackend getRrdBackend();
|
||||
|
||||
public void copyStateTo(RrdUpdater updater) throws IOException, RrdException;
|
||||
|
||||
public RrdAllocator getRrdAllocator();
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Class to represent data source values for the given timestamp. Objects of this
|
||||
* class are never created directly (no public constructor is provided). To learn more how
|
||||
* to update RRDs, see RRDTool's
|
||||
* <a href="../../../../man/rrdupdate.html" target="man">rrdupdate man page</a>.
|
||||
* <p>
|
||||
* To update a RRD with JRobin use the following procedure:
|
||||
* <p>
|
||||
* <ol>
|
||||
* <li>Obtain empty Sample object by calling method {@link RrdDb#createSample(long)
|
||||
* createSample()} on respective {@link RrdDb RrdDb} object.
|
||||
* <li>Adjust Sample timestamp if necessary (see {@link #setTime(long) setTime()} method).
|
||||
* <li>Supply data source values (see {@link #setValue(String, double) setValue()}).
|
||||
* <li>Call Sample's {@link #update() update()} method.
|
||||
* </ol>
|
||||
* <p>
|
||||
* Newly created Sample object contains all data source values set to 'unknown'.
|
||||
* You should specifify only 'known' data source values. However, if you want to specify
|
||||
* 'unknown' values too, use <code>Double.NaN</code>.
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
public class Sample {
|
||||
private RrdDb parentDb;
|
||||
private long time;
|
||||
private String[] dsNames;
|
||||
private double[] values;
|
||||
|
||||
Sample(final RrdDb parentDb, final long time) throws IOException {
|
||||
this.parentDb = parentDb;
|
||||
this.time = time;
|
||||
this.dsNames = parentDb.getDsNames();
|
||||
values = new double[dsNames.length];
|
||||
clearCurrentValues();
|
||||
}
|
||||
|
||||
private Sample clearCurrentValues() {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = Double.NaN;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets single data source value in the sample.
|
||||
*
|
||||
* @param dsName Data source name.
|
||||
* @param value Data source value.
|
||||
* @return This <code>Sample</code> object
|
||||
* @throws RrdException Thrown if invalid data source name is supplied.
|
||||
*/
|
||||
public Sample setValue(final String dsName, final double value) throws RrdException {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (dsNames[i].equals(dsName)) {
|
||||
values[i] = value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
throw new RrdException("Datasource " + dsName + " not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets single datasource value using data source index. Data sources are indexed by
|
||||
* the order specified during RRD creation (zero-based).
|
||||
*
|
||||
* @param i Data source index
|
||||
* @param value Data source values
|
||||
* @return This <code>Sample</code> object
|
||||
* @throws RrdException Thrown if data source index is invalid.
|
||||
*/
|
||||
public Sample setValue(final int i, final double value) throws RrdException {
|
||||
if (i < values.length) {
|
||||
values[i] = value;
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Sample datasource index " + i + " out of bounds");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets some (possibly all) data source values in bulk. Data source values are
|
||||
* assigned in the order of their definition inside the RRD.
|
||||
*
|
||||
* @param values Data source values.
|
||||
* @return This <code>Sample</code> object
|
||||
* @throws RrdException Thrown if the number of supplied values is zero or greater
|
||||
* than the number of data sources defined in the RRD.
|
||||
*/
|
||||
public Sample setValues(final double[] values) throws RrdException {
|
||||
if (values.length <= this.values.length) {
|
||||
System.arraycopy(values, 0, this.values, 0, values.length);
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Invalid number of values specified (found " + values.length + ", only " + dsNames.length + " allowed)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all current data source values in the sample.
|
||||
*
|
||||
* @return Data source values.
|
||||
*/
|
||||
public double[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sample timestamp (in seconds, without milliseconds).
|
||||
*
|
||||
* @return Sample timestamp.
|
||||
*/
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sample timestamp. Timestamp should be defined in seconds (without milliseconds).
|
||||
*
|
||||
* @param time New sample timestamp.
|
||||
* @return This <code>Sample</code> object
|
||||
*/
|
||||
public Sample setTime(final long time) {
|
||||
this.time = time;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all data source names. If you try to set value for the data source
|
||||
* name not in this array, an exception is thrown.
|
||||
*
|
||||
* @return Acceptable data source names.
|
||||
*/
|
||||
public String[] getDsNames() {
|
||||
return dsNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sample timestamp and data source values in a fashion similar to RRDTool.
|
||||
* Argument string should be composed in the following way:
|
||||
* <code>timestamp:value1:value2:...:valueN</code>.
|
||||
* <p>
|
||||
* You don't have to supply all datasource values. Unspecified values will be treated
|
||||
* as unknowns. To specify unknown value in the argument string, use letter 'U'.
|
||||
*
|
||||
* @param timeAndValues String made by concatenating sample timestamp with corresponding
|
||||
* data source values delmited with colons. For example:<p>
|
||||
* <pre>
|
||||
* 1005234132:12.2:35.6:U:24.5
|
||||
* NOW:12.2:35.6:U:24.5
|
||||
* </pre>
|
||||
* 'N' stands for the current timestamp (can be replaced with 'NOW')<p>
|
||||
* Method will throw an exception if timestamp is invalid (cannot be parsed as Long, and is not 'N'
|
||||
* or 'NOW'). Datasource value which cannot be parsed as 'double' will be silently set to NaN.<p>
|
||||
* @return This <code>Sample</code> object
|
||||
* @throws RrdException Thrown if too many datasource values are supplied
|
||||
*/
|
||||
public Sample set(final String timeAndValues) throws RrdException {
|
||||
final StringTokenizer tokenizer = new StringTokenizer(timeAndValues, ":", false);
|
||||
final int tokenCount = tokenizer.countTokens();
|
||||
if (tokenCount > values.length + 1) {
|
||||
throw new RrdException("Invalid number of values specified (found " + values.length + ", " + dsNames.length + " allowed)");
|
||||
}
|
||||
final String timeToken = tokenizer.nextToken();
|
||||
try {
|
||||
time = Long.parseLong(timeToken);
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
if (timeToken.equalsIgnoreCase("N") || timeToken.equalsIgnoreCase("NOW")) {
|
||||
time = Util.getTime();
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Invalid sample timestamp: " + timeToken);
|
||||
}
|
||||
}
|
||||
for (int i = 0; tokenizer.hasMoreTokens(); i++) {
|
||||
try {
|
||||
values[i] = Double.parseDouble(tokenizer.nextToken());
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
// NOP, value is already set to NaN
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores sample in the corresponding RRD. If the update operation succeedes,
|
||||
* all datasource values in the sample will be set to Double.NaN (unknown) values.
|
||||
*
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
* @throws RrdException Thrown in case of JRobin related error.
|
||||
*/
|
||||
public void update() throws IOException, RrdException {
|
||||
parentDb.store(this);
|
||||
clearCurrentValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates sample with the timestamp and data source values supplied
|
||||
* in the argument string and stores sample in the corresponding RRD.
|
||||
* This method is just a shortcut for:</p>
|
||||
* <pre>
|
||||
* set(timeAndValues);
|
||||
* update();
|
||||
* </pre>
|
||||
*
|
||||
* @param timeAndValues String made by concatenating sample timestamp with corresponding
|
||||
* data source values delmited with colons. For example:<br>
|
||||
* <code>1005234132:12.2:35.6:U:24.5</code><br>
|
||||
* <code>NOW:12.2:35.6:U:24.5</code>
|
||||
* @throws IOException Thrown in case of I/O error.
|
||||
* @throws RrdException Thrown in case of JRobin related error.
|
||||
*/
|
||||
public void setAndUpdate(final String timeAndValues) throws IOException, RrdException {
|
||||
set(timeAndValues);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps sample content using the syntax of RRDTool's update command.
|
||||
*
|
||||
* @return Sample dump.
|
||||
*/
|
||||
public String dump() {
|
||||
final StringBuffer buffer = new StringBuffer("update \"");
|
||||
buffer.append(parentDb.getRrdBackend().getPath()).append("\" ").append(time);
|
||||
for (final double value : values) {
|
||||
buffer.append(":");
|
||||
buffer.append(Util.formatDouble(value, "U", false));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
String getRrdToolCommand() {
|
||||
return dump();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "@" + "[parentDb=" + parentDb + ",time=" + new Date(time * 1000L) + ",dsNames=[" + printList(dsNames) + "],values=[" + printList(values) + "]]";
|
||||
}
|
||||
|
||||
private String printList(final Object[] dsNames) {
|
||||
if (dsNames == null) return "null";
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < dsNames.length; i++) {
|
||||
if (i == dsNames.length - 1) {
|
||||
sb.append(dsNames[i]);
|
||||
} else {
|
||||
sb.append(dsNames[i]).append(", ");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String printList(final double[] values) {
|
||||
if (values == null) return "null";
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (i == values.length - 1) {
|
||||
sb.append(values[i]);
|
||||
} else {
|
||||
sb.append(values[i]).append(", ");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
|
||||
public final class SyncManager {
|
||||
private int m_syncPeriod = RrdNioBackendFactory.DEFAULT_SYNC_PERIOD;
|
||||
private Timer m_timer = null;
|
||||
private Map<RrdNioBackend,TimerTask> m_tasks = new HashMap<RrdNioBackend,TimerTask>();
|
||||
|
||||
public SyncManager(final int syncPeriod) {
|
||||
m_syncPeriod = syncPeriod;
|
||||
}
|
||||
|
||||
public int getSyncPeriod() {
|
||||
return m_syncPeriod;
|
||||
}
|
||||
|
||||
public void setSyncPeriod(final int syncPeriod) {
|
||||
m_syncPeriod = syncPeriod;
|
||||
synchronized(m_tasks) {
|
||||
final Timer oldTimer = m_timer;
|
||||
m_timer = new SyncTimer();
|
||||
for (final RrdNioBackend backend : m_tasks.keySet()) {
|
||||
m_tasks.get(backend).cancel();
|
||||
scheduleTask(backend);
|
||||
}
|
||||
cancelTimer(oldTimer);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(final RrdNioBackend rrdNioBackend) {
|
||||
synchronized(m_tasks) {
|
||||
if (m_tasks.size() == 0) {
|
||||
m_timer = new SyncTimer();
|
||||
}
|
||||
scheduleTask(rrdNioBackend);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(final RrdNioBackend rrdNioBackend) {
|
||||
synchronized (m_tasks) {
|
||||
final TimerTask oldTask = m_tasks.remove(rrdNioBackend);
|
||||
if (oldTask != null) oldTask.cancel();
|
||||
if (m_tasks.size() == 0) {
|
||||
cancelTimer(m_timer);
|
||||
m_timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
synchronized(m_tasks) {
|
||||
for (final Map.Entry<RrdNioBackend, TimerTask> entry : m_tasks.entrySet()) {
|
||||
entry.getValue().cancel();
|
||||
}
|
||||
cancelTimer(m_timer);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelTimer(final Timer timer) {
|
||||
if (timer == null) return;
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
}
|
||||
|
||||
private void scheduleTask(final RrdNioBackend rrdNioBackend) {
|
||||
final TimerTask task = new SyncTimerTask(rrdNioBackend);
|
||||
if (m_tasks.containsKey(rrdNioBackend)) {
|
||||
m_tasks.get(rrdNioBackend).cancel();
|
||||
}
|
||||
m_tasks.put(rrdNioBackend, task);
|
||||
m_timer.schedule(task, getSyncPeriod() * 1000L, getSyncPeriod() * 1000L);
|
||||
}
|
||||
|
||||
Timer getTimer() {
|
||||
return m_timer;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class SyncTimer extends Timer {
|
||||
private static AtomicInteger m_serialNumber = new AtomicInteger();
|
||||
|
||||
public SyncTimer() {
|
||||
super("SyncManager-" + m_serialNumber.getAndIncrement(), true);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.util.TimerTask;
|
||||
|
||||
public final class SyncTimerTask extends TimerTask {
|
||||
private final RrdNioBackend m_rrdNioBackend;
|
||||
|
||||
SyncTimerTask(final RrdNioBackend rrdNioBackend) {
|
||||
m_rrdNioBackend = rrdNioBackend;
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
m_rrdNioBackend.sync();
|
||||
}
|
||||
}
|
||||
@@ -1,757 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import org.jrobin.core.timespec.TimeParser;
|
||||
import org.jrobin.core.timespec.TimeSpec;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Class defines various utility functions used in JRobin.
|
||||
*
|
||||
* @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
|
||||
*/
|
||||
public class Util {
|
||||
|
||||
public static final long MAX_LONG = Long.MAX_VALUE;
|
||||
public static final long MIN_LONG = -Long.MAX_VALUE;
|
||||
|
||||
public static final double MAX_DOUBLE = Double.MAX_VALUE;
|
||||
public static final double MIN_DOUBLE = -Double.MAX_VALUE;
|
||||
|
||||
// pattern RRDTool uses to format doubles in XML files
|
||||
static final String PATTERN = "0.0000000000E00";
|
||||
// directory under $USER_HOME used for demo graphs storing
|
||||
static final String JROBIN_DIR = "jrobin-demo";
|
||||
|
||||
static final DecimalFormat df;
|
||||
|
||||
static {
|
||||
df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.ENGLISH);
|
||||
df.applyPattern(PATTERN);
|
||||
df.setPositivePrefix("+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of long primitives to an array of doubles.
|
||||
*
|
||||
* @param array input array of long values.
|
||||
* @return Same array but with all values as double.
|
||||
*/
|
||||
public static double[] toDoubleArray(final long[] array) {
|
||||
double[] values = new double[ array.length ];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
values[i] = array[i];
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current timestamp in seconds (without milliseconds). Returned timestamp
|
||||
* is obtained with the following expression:
|
||||
* <p>
|
||||
* <code>(System.currentTimeMillis() + 500L) / 1000L</code>
|
||||
*
|
||||
* @return Current timestamp
|
||||
*/
|
||||
public static long getTime() {
|
||||
return (System.currentTimeMillis() + 500L) / 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just an alias for {@link #getTime()} method.
|
||||
*
|
||||
* @return Current timestamp (without milliseconds)
|
||||
*/
|
||||
public static long getTimestamp() {
|
||||
return getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given timestamp to the nearest whole "step". Rounded value is obtained
|
||||
* from the following expression:
|
||||
* <p>
|
||||
* <code>timestamp - timestamp % step;</code>
|
||||
*
|
||||
* @param timestamp Timestamp in seconds
|
||||
* @param step Step in seconds
|
||||
* @return "Rounded" timestamp
|
||||
*/
|
||||
public static long normalize(long timestamp, long step) {
|
||||
return timestamp - timestamp % step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the greater of two double values, but treats NaN as the smallest possible
|
||||
* value. Note that <code>Math.max()</code> behaves differently for NaN arguments.
|
||||
*
|
||||
* @param x an argument
|
||||
* @param y another argument
|
||||
* @return the lager of arguments
|
||||
*/
|
||||
public static double max(double x, double y) {
|
||||
return Double.isNaN(x) ? y : Double.isNaN(y) ? x : Math.max(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the smaller of two double values, but treats NaN as the greatest possible
|
||||
* value. Note that <code>Math.min()</code> behaves differently for NaN arguments.
|
||||
*
|
||||
* @param x an argument
|
||||
* @param y another argument
|
||||
* @return the smaller of arguments
|
||||
*/
|
||||
public static double min(double x, double y) {
|
||||
return Double.isNaN(x) ? y : Double.isNaN(y) ? x : Math.min(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates sum of two doubles, but treats NaNs as zeros.
|
||||
*
|
||||
* @param x First double
|
||||
* @param y Second double
|
||||
* @return Sum(x,y) calculated as <code>Double.isNaN(x)? y: Double.isNaN(y)? x: x + y;</code>
|
||||
*/
|
||||
public static double sum(double x, double y) {
|
||||
return Double.isNaN(x) ? y : Double.isNaN(y) ? x : x + y;
|
||||
}
|
||||
|
||||
static String formatDouble(double x, String nanString, boolean forceExponents) {
|
||||
if (Double.isNaN(x)) {
|
||||
return nanString;
|
||||
}
|
||||
if (forceExponents) {
|
||||
return df.format(x);
|
||||
}
|
||||
return "" + x;
|
||||
}
|
||||
|
||||
static String formatDouble(double x, boolean forceExponents) {
|
||||
return formatDouble(x, "" + Double.NaN, forceExponents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats double as a string using exponential notation (RRDTool like). Used for debugging
|
||||
* throught the project.
|
||||
*
|
||||
* @param x value to be formatted
|
||||
* @return string like "+1.234567E+02"
|
||||
*/
|
||||
public static String formatDouble(double x) {
|
||||
return formatDouble(x, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>Date</code> object for the given timestamp (in seconds, without
|
||||
* milliseconds)
|
||||
*
|
||||
* @param timestamp Timestamp in seconds.
|
||||
* @return Corresponding Date object.
|
||||
*/
|
||||
public static Date getDate(long timestamp) {
|
||||
return new Date(timestamp * 1000L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>Calendar</code> object for the given timestamp
|
||||
* (in seconds, without milliseconds)
|
||||
*
|
||||
* @param timestamp Timestamp in seconds.
|
||||
* @return Corresponding Calendar object.
|
||||
*/
|
||||
public static Calendar getCalendar(long timestamp) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeInMillis(timestamp * 1000L);
|
||||
return calendar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>Calendar</code> object for the given Date object
|
||||
*
|
||||
* @param date Date object
|
||||
* @return Corresponding Calendar object.
|
||||
*/
|
||||
public static Calendar getCalendar(Date date) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
return calendar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns timestamp (unix epoch) for the given Date object
|
||||
*
|
||||
* @param date Date object
|
||||
* @return Corresponding timestamp (without milliseconds)
|
||||
*/
|
||||
public static long getTimestamp(final Date date) {
|
||||
// round to whole seconds, ignore milliseconds
|
||||
return (date.getTime() + 499L) / 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns timestamp (unix epoch) for the given Calendar object
|
||||
*
|
||||
* @param gc Calendar object
|
||||
* @return Corresponding timestamp (without milliseconds)
|
||||
*/
|
||||
public static long getTimestamp(final Calendar gc) {
|
||||
return getTimestamp(gc.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns timestamp (unix epoch) for the given year, month, day, hour and minute.
|
||||
*
|
||||
* @param year Year
|
||||
* @param month Month (zero-based)
|
||||
* @param day Day in month
|
||||
* @param hour Hour
|
||||
* @param min Minute
|
||||
* @return Corresponding timestamp
|
||||
*/
|
||||
public static long getTimestamp(final int year, final int month, final int day, final int hour, final int min) {
|
||||
final Calendar calendar = Calendar.getInstance();
|
||||
calendar.clear();
|
||||
calendar.set(year, month, day, hour, min);
|
||||
return Util.getTimestamp(calendar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns timestamp (unix epoch) for the given year, month and day.
|
||||
*
|
||||
* @param year Year
|
||||
* @param month Month (zero-based)
|
||||
* @param day Day in month
|
||||
* @return Corresponding timestamp
|
||||
*/
|
||||
public static long getTimestamp(int year, int month, int day) {
|
||||
return Util.getTimestamp(year, month, day, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses at-style time specification and returns the corresponding timestamp. For example:<p>
|
||||
* <pre>
|
||||
* long t = Util.getTimestamp("now-1d");
|
||||
* </pre>
|
||||
*
|
||||
* @param atStyleTimeSpec at-style time specification. For the complete explanation of the syntax
|
||||
* allowed see RRDTool's <code>rrdfetch</code> man page.<p>
|
||||
* @return timestamp in seconds since epoch.
|
||||
* @throws RrdException Thrown if invalid time specification is supplied.
|
||||
*/
|
||||
public static long getTimestamp(final String atStyleTimeSpec) throws RrdException {
|
||||
final TimeSpec timeSpec = new TimeParser(atStyleTimeSpec).parse();
|
||||
return timeSpec.getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses two related at-style time specifications and returns corresponding timestamps. For example:<p>
|
||||
* <pre>
|
||||
* long[] t = Util.getTimestamps("end-1d","now");
|
||||
* </pre>
|
||||
*
|
||||
* @param atStyleTimeSpec1 Starting at-style time specification. For the complete explanation of the syntax
|
||||
* allowed see RRDTool's <code>rrdfetch</code> man page.<p>
|
||||
* @param atStyleTimeSpec2 Ending at-style time specification. For the complete explanation of the syntax
|
||||
* allowed see RRDTool's <code>rrdfetch</code> man page.<p>
|
||||
* @return An array of two longs representing starting and ending timestamp in seconds since epoch.
|
||||
* @throws RrdException Thrown if any input time specification is invalid.
|
||||
*/
|
||||
public static long[] getTimestamps(final String atStyleTimeSpec1, final String atStyleTimeSpec2) throws RrdException {
|
||||
final TimeSpec timeSpec1 = new TimeParser(atStyleTimeSpec1).parse();
|
||||
final TimeSpec timeSpec2 = new TimeParser(atStyleTimeSpec2).parse();
|
||||
return TimeSpec.getTimestamps(timeSpec1, timeSpec2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses input string as a double value. If the value cannot be parsed, Double.NaN
|
||||
* is returned (NumberFormatException is never thrown).
|
||||
*
|
||||
* @param valueStr String representing double value
|
||||
* @return a double corresponding to the input string
|
||||
*/
|
||||
public static double parseDouble(final String valueStr) {
|
||||
double value;
|
||||
try {
|
||||
value = Double.parseDouble(valueStr);
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
value = Double.NaN;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string can be parsed as double.
|
||||
*
|
||||
* @param s Input string
|
||||
* @return <code>true</code> if the string can be parsed as double, <code>false</code> otherwise
|
||||
*/
|
||||
public static boolean isDouble(final String s) {
|
||||
try {
|
||||
Double.parseDouble(s);
|
||||
return true;
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses input string as a boolean value. The parser is case insensitive.
|
||||
*
|
||||
* @param valueStr String representing boolean value
|
||||
* @return <code>true</code>, if valueStr equals to 'true', 'on', 'yes', 'y' or '1';
|
||||
* <code>false</code> in all other cases.
|
||||
*/
|
||||
public static boolean parseBoolean(final String valueStr) {
|
||||
return valueStr.equalsIgnoreCase("true") ||
|
||||
valueStr.equalsIgnoreCase("on") ||
|
||||
valueStr.equalsIgnoreCase("yes") ||
|
||||
valueStr.equalsIgnoreCase("y") ||
|
||||
valueStr.equalsIgnoreCase("1");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses input string as color. The color string should be of the form #RRGGBB (no alpha specified,
|
||||
* opaque color) or #RRGGBBAA (alpa specified, transparent colors). Leading character '#' is
|
||||
* optional.
|
||||
*
|
||||
* @param valueStr Input string, for example #FFAA24, #AABBCC33, 010203 or ABC13E4F
|
||||
* @return Paint object
|
||||
* @throws RrdException If the input string is not 6 or 8 characters long (without optional '#')
|
||||
*/
|
||||
public static Paint parseColor(final String valueStr) throws RrdException {
|
||||
final String c = valueStr.startsWith("#") ? valueStr.substring(1) : valueStr;
|
||||
if (c.length() != 6 && c.length() != 8) {
|
||||
throw new RrdException("Invalid color specification: " + valueStr);
|
||||
}
|
||||
final String r = c.substring(0, 2), g = c.substring(2, 4), b = c.substring(4, 6);
|
||||
if (c.length() == 6) {
|
||||
return new Color(Integer.parseInt(r, 16), Integer.parseInt(g, 16), Integer.parseInt(b, 16));
|
||||
}
|
||||
else {
|
||||
final String a = c.substring(6);
|
||||
return new Color(Integer.parseInt(r, 16), Integer.parseInt(g, 16),
|
||||
Integer.parseInt(b, 16), Integer.parseInt(a, 16));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns file system separator string.
|
||||
*
|
||||
* @return File system separator ("/" on Unix, "\" on Windows)
|
||||
*/
|
||||
public static String getFileSeparator() {
|
||||
return System.getProperty("file.separator");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to user's home directory.
|
||||
*
|
||||
* @return Path to users home directory, with file separator appended.
|
||||
*/
|
||||
public static String getUserHomeDirectory() {
|
||||
return System.getProperty("user.home") + getFileSeparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to directory used for placement of JRobin demo graphs and creates it
|
||||
* if necessary.
|
||||
*
|
||||
* @return Path to demo directory (defaults to $HOME/jrobin/) if directory exists or
|
||||
* was successfully created. Null if such directory could not be created.
|
||||
*/
|
||||
public static String getJRobinDemoDirectory() {
|
||||
final String homeDirPath = getUserHomeDirectory() + JROBIN_DIR + getFileSeparator();
|
||||
final File homeDirFile = new File(homeDirPath);
|
||||
return (homeDirFile.exists() || homeDirFile.mkdirs()) ? homeDirPath : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns full path to the file stored in the demo directory of JRobin
|
||||
*
|
||||
* @param filename Partial path to the file stored in the demo directory of JRobin
|
||||
* (just name and extension, without parent directories)
|
||||
* @return Full path to the file
|
||||
*/
|
||||
public static String getJRobinDemoPath(final String filename) {
|
||||
final String demoDir = getJRobinDemoDirectory();
|
||||
if (demoDir != null) {
|
||||
return demoDir + filename;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean sameFilePath(final String path1, final String path2) throws IOException {
|
||||
final File file1 = new File(path1);
|
||||
final File file2 = new File(path2);
|
||||
return file1.getCanonicalPath().equals(file2.getCanonicalPath());
|
||||
}
|
||||
|
||||
static int getMatchingDatasourceIndex(final RrdDb rrd1, final int dsIndex, final RrdDb rrd2) throws IOException {
|
||||
final String dsName = rrd1.getDatasource(dsIndex).getDsName();
|
||||
try {
|
||||
return rrd2.getDsIndex(dsName);
|
||||
}
|
||||
catch (final RrdException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int getMatchingArchiveIndex(final RrdDb rrd1, final int arcIndex, final RrdDb rrd2) throws IOException {
|
||||
final Archive archive = rrd1.getArchive(arcIndex);
|
||||
final String consolFun = archive.getConsolFun();
|
||||
final int steps = archive.getSteps();
|
||||
try {
|
||||
return rrd2.getArcIndex(consolFun, steps);
|
||||
}
|
||||
catch (final RrdException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static String getTmpFilename() throws IOException {
|
||||
return File.createTempFile("JROBIN_", ".tmp").getCanonicalPath();
|
||||
}
|
||||
|
||||
static final String ISO_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; // ISO
|
||||
|
||||
/**
|
||||
* Creates Calendar object from a string. The string should represent
|
||||
* either a long integer (UNIX timestamp in seconds without milliseconds,
|
||||
* like "1002354657") or a human readable date string in the format "yyyy-MM-dd HH:mm:ss"
|
||||
* (like "2004-02-25 12:23:45").
|
||||
*
|
||||
* @param timeStr Input string
|
||||
* @return Calendar object
|
||||
*/
|
||||
public static Calendar getCalendar(final String timeStr) {
|
||||
// try to parse it as long
|
||||
try {
|
||||
return Util.getCalendar(Long.parseLong(timeStr));
|
||||
}
|
||||
catch (final NumberFormatException nfe) {
|
||||
// not a long timestamp, try to parse it as data
|
||||
final SimpleDateFormat df = new SimpleDateFormat(ISO_DATE_FORMAT);
|
||||
df.setLenient(false);
|
||||
try {
|
||||
return Util.getCalendar(df.parse(timeStr));
|
||||
}
|
||||
catch (final ParseException pe) {
|
||||
throw new IllegalArgumentException("Time/date not in " + ISO_DATE_FORMAT + " format: " + timeStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Various DOM utility functions
|
||||
*/
|
||||
public static class Xml {
|
||||
public static Node[] getChildNodes(final Node parentNode) {
|
||||
return getChildNodes(parentNode, null);
|
||||
}
|
||||
|
||||
public static Node[] getChildNodes(final Node parentNode, final String childName) {
|
||||
final ArrayList<Node> nodes = new ArrayList<Node>();
|
||||
final NodeList nodeList = parentNode.getChildNodes();
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
final Node node = nodeList.item(i);
|
||||
if (childName == null || node.getNodeName().equals(childName)) {
|
||||
nodes.add(node);
|
||||
}
|
||||
}
|
||||
return nodes.toArray(new Node[0]);
|
||||
}
|
||||
|
||||
public static Node getFirstChildNode(final Node parentNode, final String childName) throws RrdException {
|
||||
final Node[] childs = getChildNodes(parentNode, childName);
|
||||
if (childs.length > 0) {
|
||||
return childs[0];
|
||||
}
|
||||
throw new RrdException("XML Error, no such child: " + childName);
|
||||
}
|
||||
|
||||
public static boolean hasChildNode(final Node parentNode, final String childName) {
|
||||
final Node[] childs = getChildNodes(parentNode, childName);
|
||||
return childs.length > 0;
|
||||
}
|
||||
|
||||
// -- Wrapper around getChildValue with trim
|
||||
public static String getChildValue(final Node parentNode, final String childName) throws RrdException {
|
||||
return getChildValue(parentNode, childName, true);
|
||||
}
|
||||
|
||||
public static String getChildValue(final Node parentNode, final String childName, final boolean trim) throws RrdException {
|
||||
final NodeList children = parentNode.getChildNodes();
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
final Node child = children.item(i);
|
||||
if (child.getNodeName().equals(childName)) {
|
||||
return getValue(child, trim);
|
||||
}
|
||||
}
|
||||
throw new RrdException("XML Error, no such child: " + childName);
|
||||
}
|
||||
|
||||
// -- Wrapper around getValue with trim
|
||||
public static String getValue(final Node node) {
|
||||
return getValue(node, true);
|
||||
}
|
||||
|
||||
public static String getValue(final Node node, final boolean trimValue) {
|
||||
String value = null;
|
||||
final Node child = node.getFirstChild();
|
||||
if (child != null) {
|
||||
value = child.getNodeValue();
|
||||
if (value != null && trimValue) {
|
||||
value = value.trim();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int getChildValueAsInt(final Node parentNode, final String childName) throws RrdException {
|
||||
final String valueStr = getChildValue(parentNode, childName);
|
||||
return Integer.parseInt(valueStr);
|
||||
}
|
||||
|
||||
public static int getValueAsInt(final Node node) {
|
||||
return Integer.parseInt(getValue(node));
|
||||
}
|
||||
|
||||
public static long getChildValueAsLong(final Node parentNode, final String childName) throws RrdException {
|
||||
final String valueStr = getChildValue(parentNode, childName);
|
||||
return Long.parseLong(valueStr);
|
||||
}
|
||||
|
||||
public static long getValueAsLong(final Node node) {
|
||||
return Long.parseLong(getValue(node));
|
||||
}
|
||||
|
||||
public static double getChildValueAsDouble(final Node parentNode, final String childName) throws RrdException {
|
||||
return Util.parseDouble(getChildValue(parentNode, childName));
|
||||
}
|
||||
|
||||
public static double getValueAsDouble(final Node node) {
|
||||
return Util.parseDouble(getValue(node));
|
||||
}
|
||||
|
||||
public static boolean getChildValueAsBoolean(final Node parentNode, final String childName) throws RrdException {
|
||||
return Util.parseBoolean(getChildValue(parentNode, childName));
|
||||
}
|
||||
|
||||
public static boolean getValueAsBoolean(final Node node) {
|
||||
return Util.parseBoolean(getValue(node));
|
||||
}
|
||||
|
||||
public static Element getRootElement(final InputSource inputSource) throws RrdException, IOException {
|
||||
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setValidating(false);
|
||||
factory.setNamespaceAware(false);
|
||||
try {
|
||||
final DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
final Document doc = builder.parse(inputSource);
|
||||
return doc.getDocumentElement();
|
||||
}
|
||||
catch (final ParserConfigurationException e) {
|
||||
throw new RrdException(e);
|
||||
}
|
||||
catch (final SAXException e) {
|
||||
throw new RrdException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Element getRootElement(final String xmlString) throws RrdException, IOException {
|
||||
return getRootElement(new InputSource(new StringReader(xmlString)));
|
||||
}
|
||||
|
||||
public static Element getRootElement(final File xmlFile) throws RrdException, IOException {
|
||||
Reader reader = null;
|
||||
try {
|
||||
reader = new FileReader(xmlFile);
|
||||
return getRootElement(new InputSource(reader));
|
||||
}
|
||||
finally {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static long lastLap = System.currentTimeMillis();
|
||||
|
||||
/**
|
||||
* Function used for debugging purposes and performance bottlenecks detection.
|
||||
* Probably of no use for end users of JRobin.
|
||||
*
|
||||
* @return String representing time in seconds since last
|
||||
* <code>getLapTime()</code> method call.
|
||||
*/
|
||||
public static String getLapTime() {
|
||||
final long newLap = System.currentTimeMillis();
|
||||
final double seconds = (newLap - lastLap) / 1000.0;
|
||||
lastLap = newLap;
|
||||
return "[" + seconds + " sec]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root directory of the JRobin distribution. Useful in some demo applications,
|
||||
* probably of no use anywhere else.
|
||||
* <p>
|
||||
* The function assumes that all JRobin .class files are placed under
|
||||
* the <root>/classes subdirectory and that all jars (libraries) are placed in the
|
||||
* <root>/lib subdirectory (the original JRobin directory structure).
|
||||
*
|
||||
* @return absolute path to JRobin's home directory
|
||||
*/
|
||||
public static String getJRobinHomeDirectory() {
|
||||
final String className = Util.class.getName().replace('.', '/');
|
||||
String uri = Util.class.getResource("/" + className + ".class").toString();
|
||||
//System.out.println(uri);
|
||||
if (uri.startsWith("file:/")) {
|
||||
uri = uri.substring(6);
|
||||
File file = new File(uri);
|
||||
// let's go 5 steps backwards
|
||||
for (int i = 0; i < 5; i++) {
|
||||
file = file.getParentFile();
|
||||
}
|
||||
uri = file.getAbsolutePath();
|
||||
}
|
||||
else if (uri.startsWith("jar:file:/")) {
|
||||
uri = uri.substring(9, uri.lastIndexOf('!'));
|
||||
File file = new File(uri);
|
||||
// let's go 2 steps backwards
|
||||
for (int i = 0; i < 2; i++) {
|
||||
file = file.getParentFile();
|
||||
}
|
||||
uri = file.getAbsolutePath();
|
||||
}
|
||||
else {
|
||||
uri = null;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two doubles but treats all NaNs as equal.
|
||||
* In Java (by default) Double.NaN == Double.NaN always returns <code>false</code>
|
||||
*
|
||||
* @param x the first value
|
||||
* @param y the second value
|
||||
* @return <code>true</code> if x and y are both equal to Double.NaN, or if x == y. <code>false</code> otherwise
|
||||
*/
|
||||
public static boolean equal(final double x, final double y) {
|
||||
return (Double.isNaN(x) && Double.isNaN(y)) || (x == y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns canonical file path for the given file path
|
||||
*
|
||||
* @param path Absolute or relative file path
|
||||
* @return Canonical file path
|
||||
* @throws IOException Thrown if canonical file path could not be resolved
|
||||
*/
|
||||
public static String getCanonicalPath(final String path) throws IOException {
|
||||
return new File(path).getCanonicalPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last modification time for the given file.
|
||||
*
|
||||
* @param file File object representing file on the disk
|
||||
* @return Last modification time in seconds (without milliseconds)
|
||||
*/
|
||||
public static long getLastModified(final String file) {
|
||||
return (new File(file).lastModified() + 500L) / 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the file with the given file name exists
|
||||
*
|
||||
* @param filename File name
|
||||
* @return <code>true</code> if file exists, <code>false</code> otherwise
|
||||
*/
|
||||
public static boolean fileExists(final String filename) {
|
||||
return new File(filename).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds max value for an array of doubles (NaNs are ignored). If all values in the array
|
||||
* are NaNs, NaN is returned.
|
||||
*
|
||||
* @param values Array of double values
|
||||
* @return max value in the array (NaNs are ignored)
|
||||
*/
|
||||
public static double max(final double[] values) {
|
||||
double max = Double.NaN;
|
||||
for (final double value : values) {
|
||||
max = Util.max(max, value);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds min value for an array of doubles (NaNs are ignored). If all values in the array
|
||||
* are NaNs, NaN is returned.
|
||||
*
|
||||
* @param values Array of double values
|
||||
* @return min value in the array (NaNs are ignored)
|
||||
*/
|
||||
public static double min(final double[] values) {
|
||||
double min = Double.NaN;
|
||||
for (final double value : values) {
|
||||
min = Util.min(min, value);
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of the C-style sprintf function. Sorry, it works only in Java5.
|
||||
*
|
||||
* @param format Format string
|
||||
* @param args Arbitrary list of arguments
|
||||
* @return Formatted string
|
||||
*/
|
||||
public static String sprintf(final String format, final Object ... args) {
|
||||
final String fmt = format.replaceAll("([^%]|^)%([^a-zA-Z%]*)l(f|g|e)", "$1%$2$3");
|
||||
return String.format(fmt, args);
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
class XmlReader extends DataImporter {
|
||||
|
||||
private Element root;
|
||||
private Node[] dsNodes, arcNodes;
|
||||
|
||||
XmlReader(String xmlFilePath) throws IOException, RrdException {
|
||||
root = Util.Xml.getRootElement(new File(xmlFilePath));
|
||||
dsNodes = Util.Xml.getChildNodes(root, "ds");
|
||||
arcNodes = Util.Xml.getChildNodes(root, "rra");
|
||||
}
|
||||
|
||||
String getVersion() throws RrdException {
|
||||
return Util.Xml.getChildValue(root, "version");
|
||||
}
|
||||
|
||||
long getLastUpdateTime() throws RrdException {
|
||||
return Util.Xml.getChildValueAsLong(root, "lastupdate");
|
||||
}
|
||||
|
||||
long getStep() throws RrdException {
|
||||
return Util.Xml.getChildValueAsLong(root, "step");
|
||||
}
|
||||
|
||||
int getDsCount() {
|
||||
return dsNodes.length;
|
||||
}
|
||||
|
||||
int getArcCount() {
|
||||
return arcNodes.length;
|
||||
}
|
||||
|
||||
String getDsName(int dsIndex) throws RrdException {
|
||||
return Util.Xml.getChildValue(dsNodes[dsIndex], "name");
|
||||
}
|
||||
|
||||
String getDsType(int dsIndex) throws RrdException {
|
||||
return Util.Xml.getChildValue(dsNodes[dsIndex], "type");
|
||||
}
|
||||
|
||||
long getHeartbeat(int dsIndex) throws RrdException {
|
||||
return Util.Xml.getChildValueAsLong(dsNodes[dsIndex], "minimal_heartbeat");
|
||||
}
|
||||
|
||||
double getMinValue(int dsIndex) throws RrdException {
|
||||
return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "min");
|
||||
}
|
||||
|
||||
double getMaxValue(int dsIndex) throws RrdException {
|
||||
return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "max");
|
||||
}
|
||||
|
||||
double getLastValue(int dsIndex) throws RrdException {
|
||||
return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "last_ds");
|
||||
}
|
||||
|
||||
double getAccumValue(int dsIndex) throws RrdException {
|
||||
return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "value");
|
||||
}
|
||||
|
||||
long getNanSeconds(int dsIndex) throws RrdException {
|
||||
return Util.Xml.getChildValueAsLong(dsNodes[dsIndex], "unknown_sec");
|
||||
}
|
||||
|
||||
String getConsolFun(int arcIndex) throws RrdException {
|
||||
return Util.Xml.getChildValue(arcNodes[arcIndex], "cf");
|
||||
}
|
||||
|
||||
double getXff(int arcIndex) throws RrdException {
|
||||
return Util.Xml.getChildValueAsDouble(arcNodes[arcIndex], "xff");
|
||||
}
|
||||
|
||||
int getSteps(int arcIndex) throws RrdException {
|
||||
return Util.Xml.getChildValueAsInt(arcNodes[arcIndex], "pdp_per_row");
|
||||
}
|
||||
|
||||
double getStateAccumValue(int arcIndex, int dsIndex) throws RrdException {
|
||||
Node cdpNode = Util.Xml.getFirstChildNode(arcNodes[arcIndex], "cdp_prep");
|
||||
Node[] dsNodes = Util.Xml.getChildNodes(cdpNode, "ds");
|
||||
return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "value");
|
||||
}
|
||||
|
||||
int getStateNanSteps(int arcIndex, int dsIndex) throws RrdException {
|
||||
Node cdpNode = Util.Xml.getFirstChildNode(arcNodes[arcIndex], "cdp_prep");
|
||||
Node[] dsNodes = Util.Xml.getChildNodes(cdpNode, "ds");
|
||||
return Util.Xml.getChildValueAsInt(dsNodes[dsIndex], "unknown_datapoints");
|
||||
}
|
||||
|
||||
int getRows(int arcIndex) throws RrdException {
|
||||
Node dbNode = Util.Xml.getFirstChildNode(arcNodes[arcIndex], "database");
|
||||
Node[] rows = Util.Xml.getChildNodes(dbNode, "row");
|
||||
return rows.length;
|
||||
}
|
||||
|
||||
double[] getValues(int arcIndex, int dsIndex) throws RrdException {
|
||||
Node dbNode = Util.Xml.getFirstChildNode(arcNodes[arcIndex], "database");
|
||||
Node[] rows = Util.Xml.getChildNodes(dbNode, "row");
|
||||
double[] values = new double[rows.length];
|
||||
for (int i = 0; i < rows.length; i++) {
|
||||
Node[] vNodes = Util.Xml.getChildNodes(rows[i], "v");
|
||||
Node vNode = vNodes[dsIndex];
|
||||
values[i] = Util.parseDouble(vNode.getFirstChild().getNodeValue().trim());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Class used as a base class for various XML template related classes. Class provides
|
||||
* methods for XML source parsing and XML tree traversing. XML source may have unlimited
|
||||
* number of placeholders (variables) in the format <code>${variable_name}</code>.
|
||||
* Methods are provided to specify variable values at runtime.
|
||||
* Note that this class has limited functionality: XML source gets parsed, and variable
|
||||
* values are collected. You have to extend this class to do something more useful.<p>
|
||||
*/
|
||||
public abstract class XmlTemplate {
|
||||
private static final String PATTERN_STRING = "\\$\\{(\\w+)\\}";
|
||||
private static final Pattern PATTERN = Pattern.compile(PATTERN_STRING);
|
||||
|
||||
protected Element root;
|
||||
private HashMap<String, Object> valueMap = new HashMap<String, Object>();
|
||||
private HashSet<Node> validatedNodes = new HashSet<Node>();
|
||||
|
||||
protected XmlTemplate(InputSource xmlSource) throws IOException, RrdException {
|
||||
root = Util.Xml.getRootElement(xmlSource);
|
||||
}
|
||||
|
||||
protected XmlTemplate(String xmlString) throws IOException, RrdException {
|
||||
root = Util.Xml.getRootElement(xmlString);
|
||||
}
|
||||
|
||||
protected XmlTemplate(File xmlFile) throws IOException, RrdException {
|
||||
root = Util.Xml.getRootElement(xmlFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all placeholder-value mappings.
|
||||
*/
|
||||
public void clearValues() {
|
||||
valueMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value for a single XML template variable. Variable name should be specified
|
||||
* without leading '${' and ending '}' placeholder markers. For example, for a placeholder
|
||||
* <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
|
||||
*
|
||||
* @param name variable name
|
||||
* @param value value to be set in the XML template
|
||||
*/
|
||||
public void setVariable(String name, String value) {
|
||||
valueMap.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value for a single XML template variable. Variable name should be specified
|
||||
* without leading '${' and ending '}' placeholder markers. For example, for a placeholder
|
||||
* <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
|
||||
*
|
||||
* @param name variable name
|
||||
* @param value value to be set in the XML template
|
||||
*/
|
||||
public void setVariable(String name, int value) {
|
||||
valueMap.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value for a single XML template variable. Variable name should be specified
|
||||
* without leading '${' and ending '}' placeholder markers. For example, for a placeholder
|
||||
* <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
|
||||
*
|
||||
* @param name variable name
|
||||
* @param value value to be set in the XML template
|
||||
*/
|
||||
public void setVariable(String name, long value) {
|
||||
valueMap.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value for a single XML template variable. Variable name should be specified
|
||||
* without leading '${' and ending '}' placeholder markers. For example, for a placeholder
|
||||
* <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
|
||||
*
|
||||
* @param name variable name
|
||||
* @param value value to be set in the XML template
|
||||
*/
|
||||
public void setVariable(String name, double value) {
|
||||
valueMap.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value for a single XML template variable. Variable name should be specified
|
||||
* without leading '${' and ending '}' placeholder markers. For example, for a placeholder
|
||||
* <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
|
||||
*
|
||||
* @param name variable name
|
||||
* @param value value to be set in the XML template
|
||||
*/
|
||||
public void setVariable(String name, Color value) {
|
||||
String r = byteToHex(value.getRed());
|
||||
String g = byteToHex(value.getGreen());
|
||||
String b = byteToHex(value.getBlue());
|
||||
String a = byteToHex(value.getAlpha());
|
||||
valueMap.put(name, "#" + r + g + b + a);
|
||||
}
|
||||
|
||||
private String byteToHex(int i) {
|
||||
String s = Integer.toHexString(i);
|
||||
while (s.length() < 2) {
|
||||
s = "0" + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value for a single XML template variable. Variable name should be specified
|
||||
* without leading '${' and ending '}' placeholder markers. For example, for a placeholder
|
||||
* <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
|
||||
*
|
||||
* @param name variable name
|
||||
* @param value value to be set in the XML template
|
||||
*/
|
||||
public void setVariable(String name, Date value) {
|
||||
setVariable(name, Util.getTimestamp(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value for a single XML template variable. Variable name should be specified
|
||||
* without leading '${' and ending '}' placeholder markers. For example, for a placeholder
|
||||
* <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
|
||||
*
|
||||
* @param name variable name
|
||||
* @param value value to be set in the XML template
|
||||
*/
|
||||
public void setVariable(String name, Calendar value) {
|
||||
setVariable(name, Util.getTimestamp(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value for a single XML template variable. Variable name should be specified
|
||||
* without leading '${' and ending '}' placeholder markers. For example, for a placeholder
|
||||
* <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
|
||||
*
|
||||
* @param name variable name
|
||||
* @param value value to be set in the XML template
|
||||
*/
|
||||
public void setVariable(String name, boolean value) {
|
||||
valueMap.put(name, "" + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the XML template to see if there are variables in there that
|
||||
* will need to be set.
|
||||
*
|
||||
* @return True if variables were detected, false if not.
|
||||
*/
|
||||
public boolean hasVariables() {
|
||||
return PATTERN.matcher(root.toString()).find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of variables that should be set in this template.
|
||||
*
|
||||
* @return List of variable names as an array of strings.
|
||||
*/
|
||||
public String[] getVariables() {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
Matcher m = PATTERN.matcher(root.toString());
|
||||
|
||||
while (m.find()) {
|
||||
String var = m.group(1);
|
||||
if (!list.contains(var)) {
|
||||
list.add(var);
|
||||
}
|
||||
}
|
||||
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
protected static Node[] getChildNodes(Node parentNode, String childName) {
|
||||
return Util.Xml.getChildNodes(parentNode, childName);
|
||||
}
|
||||
|
||||
protected static Node[] getChildNodes(Node parentNode) {
|
||||
return Util.Xml.getChildNodes(parentNode, null);
|
||||
}
|
||||
|
||||
protected static Node getFirstChildNode(Node parentNode, String childName) throws RrdException {
|
||||
return Util.Xml.getFirstChildNode(parentNode, childName);
|
||||
}
|
||||
|
||||
protected boolean hasChildNode(Node parentNode, String childName) {
|
||||
return Util.Xml.hasChildNode(parentNode, childName);
|
||||
}
|
||||
|
||||
protected String getChildValue(Node parentNode, String childName) throws RrdException {
|
||||
return getChildValue(parentNode, childName, true);
|
||||
}
|
||||
|
||||
protected String getChildValue(Node parentNode, String childName, boolean trim) throws RrdException {
|
||||
String value = Util.Xml.getChildValue(parentNode, childName, trim);
|
||||
return resolveMappings(value);
|
||||
}
|
||||
|
||||
protected String getValue(Node parentNode) {
|
||||
return getValue(parentNode, true);
|
||||
}
|
||||
|
||||
protected String getValue(Node parentNode, boolean trim) {
|
||||
String value = Util.Xml.getValue(parentNode, trim);
|
||||
return resolveMappings(value);
|
||||
}
|
||||
|
||||
private String resolveMappings(String templateValue) {
|
||||
if (templateValue == null) {
|
||||
return null;
|
||||
}
|
||||
Matcher matcher = PATTERN.matcher(templateValue);
|
||||
StringBuffer result = new StringBuffer();
|
||||
int lastMatchEnd = 0;
|
||||
while (matcher.find()) {
|
||||
String var = matcher.group(1);
|
||||
if (valueMap.containsKey(var)) {
|
||||
// mapping found
|
||||
result.append(templateValue.substring(lastMatchEnd, matcher.start()));
|
||||
result.append(valueMap.get(var).toString());
|
||||
lastMatchEnd = matcher.end();
|
||||
}
|
||||
else {
|
||||
// no mapping found - this is illegal
|
||||
// throw runtime exception
|
||||
throw new IllegalArgumentException("No mapping found for template variable ${" + var + "}");
|
||||
}
|
||||
}
|
||||
result.append(templateValue.substring(lastMatchEnd));
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
protected int getChildValueAsInt(Node parentNode, String childName) throws RrdException {
|
||||
String valueStr = getChildValue(parentNode, childName);
|
||||
return Integer.parseInt(valueStr);
|
||||
}
|
||||
|
||||
protected int getValueAsInt(Node parentNode) {
|
||||
String valueStr = getValue(parentNode);
|
||||
return Integer.parseInt(valueStr);
|
||||
}
|
||||
|
||||
protected long getChildValueAsLong(Node parentNode, String childName) throws RrdException {
|
||||
String valueStr = getChildValue(parentNode, childName);
|
||||
return Long.parseLong(valueStr);
|
||||
}
|
||||
|
||||
protected long getValueAsLong(Node parentNode) {
|
||||
String valueStr = getValue(parentNode);
|
||||
return Long.parseLong(valueStr);
|
||||
}
|
||||
|
||||
protected double getChildValueAsDouble(Node parentNode, String childName) throws RrdException {
|
||||
String valueStr = getChildValue(parentNode, childName);
|
||||
return Util.parseDouble(valueStr);
|
||||
}
|
||||
|
||||
protected double getValueAsDouble(Node parentNode) {
|
||||
String valueStr = getValue(parentNode);
|
||||
return Util.parseDouble(valueStr);
|
||||
}
|
||||
|
||||
protected boolean getChildValueAsBoolean(Node parentNode, String childName) throws RrdException {
|
||||
String valueStr = getChildValue(parentNode, childName);
|
||||
return Util.parseBoolean(valueStr);
|
||||
}
|
||||
|
||||
protected boolean getValueAsBoolean(Node parentNode) {
|
||||
String valueStr = getValue(parentNode);
|
||||
return Util.parseBoolean(valueStr);
|
||||
}
|
||||
|
||||
protected Paint getValueAsColor(Node parentNode) throws RrdException {
|
||||
String rgbStr = getValue(parentNode);
|
||||
return Util.parseColor(rgbStr);
|
||||
}
|
||||
|
||||
protected boolean isEmptyNode(Node node) {
|
||||
// comment node or empty text node
|
||||
return node.getNodeName().equals("#comment") ||
|
||||
(node.getNodeName().equals("#text") && node.getNodeValue().trim().length() == 0);
|
||||
}
|
||||
|
||||
protected void validateTagsOnlyOnce(Node parentNode, String[] allowedChildNames) throws RrdException {
|
||||
// validate node only once
|
||||
if (validatedNodes.contains(parentNode)) {
|
||||
return;
|
||||
}
|
||||
Node[] childs = getChildNodes(parentNode);
|
||||
main:
|
||||
for (Node child : childs) {
|
||||
String childName = child.getNodeName();
|
||||
for (int j = 0; j < allowedChildNames.length; j++) {
|
||||
if (allowedChildNames[j].equals(childName)) {
|
||||
// only one such tag is allowed
|
||||
allowedChildNames[j] = "<--removed-->";
|
||||
continue main;
|
||||
}
|
||||
else if (allowedChildNames[j].equals(childName + "*")) {
|
||||
// several tags allowed
|
||||
continue main;
|
||||
}
|
||||
}
|
||||
if (!isEmptyNode(child)) {
|
||||
throw new RrdException("Unexpected tag encountered: <" + childName + ">");
|
||||
}
|
||||
}
|
||||
// everything is OK
|
||||
validatedNodes.add(parentNode);
|
||||
}
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Extremely simple utility class used to create XML documents.
|
||||
*/
|
||||
public class XmlWriter {
|
||||
static final String INDENT_STR = " ";
|
||||
|
||||
private PrintWriter writer;
|
||||
private StringBuffer indent = new StringBuffer("");
|
||||
private Stack<String> openTags = new Stack<String>();
|
||||
|
||||
/**
|
||||
* Creates XmlWriter with the specified output stream to send XML code to.
|
||||
*
|
||||
* @param stream Output stream which receives XML code
|
||||
*/
|
||||
public XmlWriter(OutputStream stream) {
|
||||
writer = new PrintWriter(stream, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens XML tag
|
||||
*
|
||||
* @param tag XML tag name
|
||||
*/
|
||||
public void startTag(String tag) {
|
||||
writer.println(indent + "<" + tag + ">");
|
||||
openTags.push(tag);
|
||||
indent.append(INDENT_STR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the corresponding XML tag
|
||||
*/
|
||||
public void closeTag() {
|
||||
String tag = openTags.pop();
|
||||
indent.setLength(indent.length() - INDENT_STR.length());
|
||||
writer.println(indent + "</" + tag + ">");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <tag>value</tag> to output stream
|
||||
*
|
||||
* @param tag XML tag name
|
||||
* @param value value to be placed between <code><tag></code> and <code></tag></code>
|
||||
*/
|
||||
public void writeTag(String tag, Object value) {
|
||||
if (value != null) {
|
||||
writer.println(indent + "<" + tag + ">" +
|
||||
escape(value.toString()) + "</" + tag + ">");
|
||||
}
|
||||
else {
|
||||
writer.println(indent + "<" + tag + "></" + tag + ">");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <tag>value</tag> to output stream
|
||||
*
|
||||
* @param tag XML tag name
|
||||
* @param value value to be placed between <code><tag></code> and <code></tag></code>
|
||||
*/
|
||||
public void writeTag(String tag, int value) {
|
||||
writeTag(tag, "" + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <tag>value</tag> to output stream
|
||||
*
|
||||
* @param tag XML tag name
|
||||
* @param value value to be placed between <code><tag></code> and <code></tag></code>
|
||||
*/
|
||||
public void writeTag(String tag, long value) {
|
||||
writeTag(tag, "" + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <tag>value</tag> to output stream
|
||||
*
|
||||
* @param tag XML tag name
|
||||
* @param value value to be placed between <code><tag></code> and <code></tag></code>
|
||||
* @param nanString string to display if the value is NaN.
|
||||
*/
|
||||
public void writeTag(String tag, double value, String nanString) {
|
||||
writeTag(tag, Util.formatDouble(value, nanString, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <tag>value</tag> to output stream
|
||||
*
|
||||
* @param tag XML tag name
|
||||
* @param value value to be placed between <code><tag></code> and <code></tag></code>
|
||||
*/
|
||||
public void writeTag(String tag, double value) {
|
||||
writeTag(tag, Util.formatDouble(value, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <tag>value</tag> to output stream
|
||||
*
|
||||
* @param tag XML tag name
|
||||
* @param value value to be placed between <code><tag></code> and <code></tag></code>
|
||||
*/
|
||||
public void writeTag(String tag, boolean value) {
|
||||
writeTag(tag, "" + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <tag>value</tag> to output stream
|
||||
*
|
||||
* @param tag XML tag name
|
||||
* @param value value to be placed between <code><tag></code> and <code></tag></code>
|
||||
*/
|
||||
public void writeTag(String tag, Color value) {
|
||||
int rgb = value.getRGB() & 0xFFFFFF;
|
||||
writeTag(tag, "#" + Integer.toHexString(rgb).toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <tag>value</tag> to output stream
|
||||
*
|
||||
* @param tag XML tag name
|
||||
* @param value value to be placed between <code><tag></code> and <code></tag></code>
|
||||
*/
|
||||
public void writeTag(String tag, Font value) {
|
||||
startTag(tag);
|
||||
writeTag("name", value.getName());
|
||||
int style = value.getStyle();
|
||||
if ((style & Font.BOLD) != 0 && (style & Font.ITALIC) != 0) {
|
||||
writeTag("style", "BOLDITALIC");
|
||||
}
|
||||
else if ((style & Font.BOLD) != 0) {
|
||||
writeTag("style", "BOLD");
|
||||
}
|
||||
else if ((style & Font.ITALIC) != 0) {
|
||||
writeTag("style", "ITALIC");
|
||||
}
|
||||
else {
|
||||
writeTag("style", "PLAIN");
|
||||
}
|
||||
writeTag("size", value.getSize());
|
||||
closeTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <tag>value</tag> to output stream
|
||||
*
|
||||
* @param tag XML tag name
|
||||
* @param value value to be placed between <code><tag></code> and <code></tag></code>
|
||||
*/
|
||||
public void writeTag(String tag, File value) {
|
||||
writeTag(tag, value.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the output stream
|
||||
*/
|
||||
public void flush() {
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
writer.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes XML comment to output stream
|
||||
*
|
||||
* @param comment comment string
|
||||
*/
|
||||
public void writeComment(Object comment) {
|
||||
writer.println(indent + "<!-- " + escape(comment.toString()) + " -->");
|
||||
}
|
||||
|
||||
private static String escape(String s) {
|
||||
return s.replaceAll("<", "<").replaceAll(">", ">");
|
||||
}
|
||||
}
|
||||
@@ -1,426 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
/**
|
||||
* Instances of this class model an archive section of an RRD file.
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class Archive {
|
||||
|
||||
RRDatabase db;
|
||||
long offset;
|
||||
long dataOffset;
|
||||
long size;
|
||||
ConsolidationFunctionType type;
|
||||
int rowCount;
|
||||
int pdpCount;
|
||||
double xff;
|
||||
ArrayList<CDPStatusBlock> cdpStatusBlocks;
|
||||
int currentRow;
|
||||
|
||||
private double[][] values;
|
||||
|
||||
Archive(RRDatabase db) throws IOException,RrdException {
|
||||
|
||||
this.db = db;
|
||||
|
||||
RRDFile file = db.rrdFile;
|
||||
|
||||
offset = file.getFilePointer();
|
||||
type =
|
||||
ConsolidationFunctionType.get(file.readString(Constants.CF_NAM_SIZE));
|
||||
rowCount = file.readInt();
|
||||
pdpCount = file.readInt();
|
||||
|
||||
file.align();
|
||||
|
||||
xff = file.readDouble();
|
||||
|
||||
// Skip rest of rra_def_t.par[]
|
||||
file.align();
|
||||
file.skipBytes(72);
|
||||
|
||||
size = file.getFilePointer() - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of function used to calculate the consolidated data point.
|
||||
*
|
||||
* @return the type of function used to calculate the consolidated data point.
|
||||
*/
|
||||
public ConsolidationFunctionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
void loadCDPStatusBlocks(RRDFile file, int numBlocks) throws IOException, RrdException {
|
||||
|
||||
cdpStatusBlocks = new ArrayList<CDPStatusBlock>();
|
||||
|
||||
for (int i = 0; i < numBlocks; i++) {
|
||||
cdpStatusBlocks.add(new CDPStatusBlock(file));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>CDPStatusBlock</code> at the specified position in this archive.
|
||||
*
|
||||
* @param index index of <code>CDPStatusBlock</code> to return.
|
||||
* @return the <code>CDPStatusBlock</code> at the specified position in this archive.
|
||||
*/
|
||||
public CDPStatusBlock getCDPStatusBlock(int index) {
|
||||
return cdpStatusBlocks.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the CDP status blocks in this archive in proper sequence.
|
||||
*
|
||||
* @return an iterator over the CDP status blocks in this archive in proper sequence.
|
||||
* @see CDPStatusBlock
|
||||
*/
|
||||
public Iterator<CDPStatusBlock> getCDPStatusBlocks() {
|
||||
return cdpStatusBlocks.iterator();
|
||||
}
|
||||
|
||||
void loadCurrentRow(RRDFile file) throws IOException,RrdException {
|
||||
currentRow = file.readInt();
|
||||
}
|
||||
|
||||
void loadData(RRDFile file, int dsCount) throws IOException {
|
||||
|
||||
dataOffset = file.getFilePointer();
|
||||
|
||||
// Skip over the data to position ourselves at the start of the next archive
|
||||
file.skipBytes(8 * rowCount * dsCount);
|
||||
}
|
||||
|
||||
DataChunk loadData(DataChunk chunk) throws IOException,RrdException {
|
||||
|
||||
Calendar end = Calendar.getInstance();
|
||||
Calendar start = (Calendar) end.clone();
|
||||
|
||||
start.add(Calendar.DATE, -1);
|
||||
|
||||
loadData(chunk, start.getTime().getTime() / 1000,
|
||||
end.getTime().getTime() / 1000);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void loadData(DataChunk chunk, long startTime, long endTime)
|
||||
throws IOException,RrdException {
|
||||
|
||||
long pointer;
|
||||
|
||||
if (chunk.start < 0) {
|
||||
pointer = currentRow + 1;
|
||||
}
|
||||
else {
|
||||
pointer = currentRow + chunk.start + 1;
|
||||
}
|
||||
|
||||
db.rrdFile.ras.seek(dataOffset + (pointer * 8));
|
||||
//cat.debug("Archive Base: " + dataOffset + " Archive Pointer: " + pointer);
|
||||
//cat.debug("Start Offset: " + chunk.start + " End Offset: "
|
||||
// + (rowCount - chunk.end));
|
||||
|
||||
double[][] data = chunk.data;
|
||||
|
||||
/*
|
||||
* This is also terrible - cleanup - CT
|
||||
*/
|
||||
int row = 0;
|
||||
for (int i = chunk.start; i < rowCount - chunk.end; i++, row++) {
|
||||
if (i < 0) { // no valid data yet
|
||||
for (int ii = 0; ii < chunk.dsCount; ii++) {
|
||||
data[row][ii] = Double.NaN;
|
||||
}
|
||||
}
|
||||
else if (i >= rowCount) { // past valid data area
|
||||
for (int ii = 0; ii < chunk.dsCount; ii++) {
|
||||
data[row][ii] = Double.NaN;
|
||||
}
|
||||
}
|
||||
else { // inside the valid are but the pointer has to be wrapped
|
||||
if (pointer >= rowCount) {
|
||||
pointer -= rowCount;
|
||||
|
||||
db.rrdFile.ras.seek(dataOffset + (pointer * 8));
|
||||
}
|
||||
|
||||
for (int ii = 0; ii < chunk.dsCount; ii++) {
|
||||
data[row][ii] = db.rrdFile.readDouble();
|
||||
}
|
||||
|
||||
pointer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printInfo(PrintStream s, NumberFormat numberFormat, int index) {
|
||||
|
||||
StringBuffer sb = new StringBuffer("rra[");
|
||||
|
||||
sb.append(index);
|
||||
s.print(sb);
|
||||
s.print("].cf = \"");
|
||||
s.print(type);
|
||||
s.println("\"");
|
||||
s.print(sb);
|
||||
s.print("].rows = ");
|
||||
s.println(rowCount);
|
||||
s.print(sb);
|
||||
s.print("].pdp_per_row = ");
|
||||
s.println(pdpCount);
|
||||
s.print(sb);
|
||||
s.print("].xff = ");
|
||||
s.println(xff);
|
||||
sb.append("].cdp_prep[");
|
||||
|
||||
int cdpIndex = 0;
|
||||
|
||||
for (Iterator<CDPStatusBlock> i = cdpStatusBlocks.iterator(); i.hasNext();) {
|
||||
CDPStatusBlock cdp = i.next();
|
||||
|
||||
s.print(sb);
|
||||
s.print(cdpIndex);
|
||||
s.print("].value = ");
|
||||
|
||||
double value = cdp.value;
|
||||
|
||||
s.println(Double.isNaN(value)
|
||||
? "NaN"
|
||||
: numberFormat.format(value));
|
||||
s.print(sb);
|
||||
s.print(cdpIndex++);
|
||||
s.print("].unknown_datapoints = ");
|
||||
s.println(cdp.unknownDatapoints);
|
||||
}
|
||||
}
|
||||
|
||||
void toXml(PrintStream s) throws RrdException {
|
||||
|
||||
try {
|
||||
s.println("\t<rra>");
|
||||
s.print("\t\t<cf> ");
|
||||
s.print(type);
|
||||
s.println(" </cf>");
|
||||
s.print("\t\t<pdp_per_row> ");
|
||||
s.print(pdpCount);
|
||||
s.print(" </pdp_per_row> <!-- ");
|
||||
s.print(db.header.pdpStep * pdpCount);
|
||||
s.println(" seconds -->");
|
||||
s.print("\t\t<xff> ");
|
||||
s.print(xff);
|
||||
s.println(" </xff>");
|
||||
s.println();
|
||||
s.println("\t\t<cdp_prep>");
|
||||
|
||||
for (int i = 0; i < cdpStatusBlocks.size(); i++) {
|
||||
cdpStatusBlocks.get(i).toXml(s);
|
||||
}
|
||||
|
||||
s.println("\t\t</cdp_prep>");
|
||||
s.println("\t\t<database>");
|
||||
|
||||
long timer = -(rowCount - 1);
|
||||
int counter = 0;
|
||||
int row = currentRow;
|
||||
|
||||
db.rrdFile.ras.seek(dataOffset + (row + 1) * 16);
|
||||
|
||||
long lastUpdate = db.lastUpdate.getTime() / 1000;
|
||||
int pdpStep = db.header.pdpStep;
|
||||
NumberFormat numberFormat = new DecimalFormat("0.0000000000E0");
|
||||
SimpleDateFormat dateFormat =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
||||
|
||||
while (counter++ < rowCount) {
|
||||
row++;
|
||||
|
||||
if (row == rowCount) {
|
||||
row = 0;
|
||||
|
||||
db.rrdFile.ras.seek(dataOffset);
|
||||
}
|
||||
|
||||
long now = (lastUpdate - lastUpdate % (pdpCount * pdpStep))
|
||||
+ (timer * pdpCount * pdpStep);
|
||||
|
||||
timer++;
|
||||
|
||||
s.print("\t\t\t<!-- ");
|
||||
s.print(dateFormat.format(new Date(now * 1000)));
|
||||
s.print(" / ");
|
||||
s.print(now);
|
||||
s.print(" --> ");
|
||||
|
||||
for (int col = 0; col < db.header.dsCount; col++) {
|
||||
s.print("<v> ");
|
||||
|
||||
double value = db.rrdFile.readDouble();
|
||||
|
||||
// NumberFormat doesn't know how to handle NaN
|
||||
if (Double.isNaN(value)) {
|
||||
s.print("NaN");
|
||||
}
|
||||
else {
|
||||
s.print(numberFormat.format(value));
|
||||
}
|
||||
|
||||
s.print(" </v>");
|
||||
}
|
||||
|
||||
s.println("</row>");
|
||||
}
|
||||
|
||||
s.println("\t\t</database>");
|
||||
s.println("\t</rra>");
|
||||
}
|
||||
catch (IOException e) { // Is the best thing to do here?
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// THIS IS THE ORIGINAL CODE: BUGGY! Replaced by Sasa Markovic with a new method
|
||||
// Funny: the bug will appear only if dsCount != 2 :)
|
||||
public double[][] getValuesOriginal() throws IOException {
|
||||
if (values != null) {
|
||||
return values;
|
||||
}
|
||||
values = new double[db.header.dsCount][rowCount];
|
||||
int row = currentRow;
|
||||
db.rrdFile.ras.seek(dataOffset + (row + 1) * 16); // <----- BUG (resolved below)
|
||||
for (int counter = 0; counter < rowCount; counter++) {
|
||||
row++;
|
||||
if (row == rowCount) {
|
||||
row = 0;
|
||||
db.rrdFile.ras.seek(dataOffset);
|
||||
}
|
||||
for (int col = 0; col < db.header.dsCount; col++) {
|
||||
double value = db.rrdFile.readDouble();
|
||||
values[col][counter] = value;
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
*/
|
||||
|
||||
// Resolved bug from the original method (see above)
|
||||
public double[][] getValues() throws IOException,RrdException {
|
||||
// OK PART
|
||||
if (values != null) {
|
||||
return values;
|
||||
}
|
||||
values = new double[db.header.dsCount][rowCount];
|
||||
int row = currentRow;
|
||||
// HERE ARE THE DRAGONS!
|
||||
db.rrdFile.ras.seek(dataOffset + (row + 1) * db.header.dsCount * 8);
|
||||
// OK, TOO!
|
||||
for (int counter = 0; counter < rowCount; counter++) {
|
||||
row++;
|
||||
if (row == rowCount) {
|
||||
row = 0;
|
||||
db.rrdFile.ras.seek(dataOffset);
|
||||
}
|
||||
for (int col = 0; col < db.header.dsCount; col++) {
|
||||
double value = db.rrdFile.readDouble();
|
||||
values[col][counter] = value;
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of primary data points required for a consolidated
|
||||
* data point in this archive.
|
||||
*
|
||||
* @return the number of primary data points required for a consolidated
|
||||
* data point in this archive.
|
||||
*/
|
||||
public int getPdpCount() {
|
||||
return pdpCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of entries in this archive.
|
||||
*
|
||||
* @return the number of entries in this archive.
|
||||
*/
|
||||
public int getRowCount() {
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the X-Files Factor for this archive.
|
||||
*
|
||||
* @return the X-Files Factor for this archive.
|
||||
*/
|
||||
public double getXff() {
|
||||
return xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary the contents of this archive.
|
||||
*
|
||||
* @return a summary of the information contained in this archive.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
StringBuffer sb = new StringBuffer("[Archive: OFFSET=0x");
|
||||
|
||||
sb.append(Long.toHexString(offset));
|
||||
sb.append(", SIZE=0x");
|
||||
sb.append(Long.toHexString(size));
|
||||
sb.append(", type=");
|
||||
sb.append(type);
|
||||
sb.append(", rowCount=");
|
||||
sb.append(rowCount);
|
||||
sb.append(", pdpCount=");
|
||||
sb.append(pdpCount);
|
||||
sb.append(", xff=");
|
||||
sb.append(xff);
|
||||
sb.append(", currentRow=");
|
||||
sb.append(currentRow);
|
||||
sb.append("]");
|
||||
|
||||
for (Iterator<CDPStatusBlock> i = cdpStatusBlocks.iterator(); i.hasNext();) {
|
||||
CDPStatusBlock cdp = i.next();
|
||||
|
||||
sb.append("\n\t\t");
|
||||
sb.append(cdp.toString());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
/**
|
||||
* Instances of this class model the consolidation data point status from an RRD file.
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class CDPStatusBlock {
|
||||
|
||||
long offset;
|
||||
long size;
|
||||
int unknownDatapoints;
|
||||
double value;
|
||||
|
||||
CDPStatusBlock(RRDFile file) throws IOException, RrdException {
|
||||
|
||||
offset = file.getFilePointer();
|
||||
value = file.readDouble();
|
||||
unknownDatapoints = file.readInt();
|
||||
file.align(8);
|
||||
// Skip rest of cdp_prep_t.scratch
|
||||
file.skipBytes(64);
|
||||
|
||||
size = file.getFilePointer() - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of unknown primary data points that were integrated.
|
||||
*
|
||||
* @return the number of unknown primary data points that were integrated.
|
||||
*/
|
||||
public int getUnknownDatapoints() {
|
||||
return unknownDatapoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this consolidated data point.
|
||||
*
|
||||
* @return the value of this consolidated data point.
|
||||
*/
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
void toXml(PrintStream s) {
|
||||
|
||||
s.print("\t\t\t<ds><value> ");
|
||||
s.print(value);
|
||||
s.print(" </value> <unknown_datapoints> ");
|
||||
s.print(unknownDatapoints);
|
||||
s.println(" </unknown_datapoints></ds>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary the contents of this CDP status block.
|
||||
*
|
||||
* @return a summary of the information contained in the CDP status block.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
StringBuffer sb = new StringBuffer("[CDPStatusBlock: OFFSET=0x");
|
||||
|
||||
sb.append(Long.toHexString(offset));
|
||||
sb.append(", SIZE=0x");
|
||||
sb.append(Long.toHexString(size));
|
||||
sb.append(", unknownDatapoints=");
|
||||
sb.append(unknownDatapoints);
|
||||
sb.append(", value=");
|
||||
sb.append(value);
|
||||
sb.append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
/**
|
||||
* Class ConsolidationFunctionType
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class ConsolidationFunctionType {
|
||||
|
||||
private static final int _AVERAGE = 0;
|
||||
private static final String STR_AVERAGE = "AVERAGE";
|
||||
|
||||
/**
|
||||
* Field AVERAGE
|
||||
*/
|
||||
public static final ConsolidationFunctionType AVERAGE = new ConsolidationFunctionType(_AVERAGE);
|
||||
private static final int _MIN = 1;
|
||||
private static final String STR_MIN = "MIN";
|
||||
|
||||
/**
|
||||
* Field MIN
|
||||
*/
|
||||
public static final ConsolidationFunctionType MIN = new ConsolidationFunctionType(_MIN);
|
||||
private static final int _MAX = 2;
|
||||
private static final String STR_MAX = "MAX";
|
||||
|
||||
/**
|
||||
* Field MAX
|
||||
*/
|
||||
public static final ConsolidationFunctionType MAX = new ConsolidationFunctionType(_MAX);
|
||||
private static final int _LAST = 3;
|
||||
private static final String STR_LAST = "LAST";
|
||||
|
||||
/**
|
||||
* Field LAST
|
||||
*/
|
||||
public static final ConsolidationFunctionType LAST = new ConsolidationFunctionType(_LAST);
|
||||
private int type;
|
||||
|
||||
private ConsolidationFunctionType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <code>ConsolidationFunctionType</code> with the given name.
|
||||
*
|
||||
* @param s name of the <code>ConsolidationFunctionType</code> required.
|
||||
* @return a <code>ConsolidationFunctionType</code> with the given name.
|
||||
*/
|
||||
public static ConsolidationFunctionType get(final String s) {
|
||||
|
||||
if (STR_AVERAGE.equalsIgnoreCase(s)) {
|
||||
return AVERAGE;
|
||||
}
|
||||
else if (STR_MIN.equalsIgnoreCase(s)) {
|
||||
return MIN;
|
||||
}
|
||||
else if (STR_MAX.equalsIgnoreCase(s)) {
|
||||
return MAX;
|
||||
}
|
||||
else if (STR_LAST.equalsIgnoreCase(s)) {
|
||||
return LAST;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid ConsolidationFunctionType: " + s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this object against the specified object.
|
||||
*
|
||||
* @return <code>true</code> if the objects are the same,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean equals(final Object o) {
|
||||
|
||||
if (!(o instanceof ConsolidationFunctionType)) {
|
||||
throw new IllegalArgumentException("Not a ConsolidationFunctionType");
|
||||
}
|
||||
|
||||
return (((ConsolidationFunctionType) o).type == type)
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return type * 93;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this object.
|
||||
*
|
||||
* @return a string representation of this object.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
String strType;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case _AVERAGE:
|
||||
strType = STR_AVERAGE;
|
||||
break;
|
||||
|
||||
case _MIN:
|
||||
strType = STR_MIN;
|
||||
break;
|
||||
|
||||
case _MAX:
|
||||
strType = STR_MAX;
|
||||
break;
|
||||
|
||||
case _LAST:
|
||||
strType = STR_LAST;
|
||||
break;
|
||||
|
||||
default :
|
||||
throw new RuntimeException("This should never happen");
|
||||
}
|
||||
|
||||
return strType;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
interface Constants {
|
||||
|
||||
int DS_NAM_SIZE = 20;
|
||||
int DST_SIZE = 20;
|
||||
int CF_NAM_SIZE = 20;
|
||||
int LAST_DS_LEN = 30;
|
||||
static String COOKIE = "RRD";
|
||||
static String VERSION = "0001";
|
||||
static String VERSION3 = "0003";
|
||||
double FLOAT_COOKIE = 8.642135E130;
|
||||
static byte[] FLOAT_COOKIE_BIG_ENDIAN = {0x5B, 0x1F, 0x2B, 0x43,
|
||||
(byte) 0xC7, (byte) 0xC0, 0x25,
|
||||
0x2F};
|
||||
static byte[] FLOAT_COOKIE_LITTLE_ENDIAN = {0x2F, 0x25, (byte) 0xC0,
|
||||
(byte) 0xC7, 0x43, 0x2B, 0x1F,
|
||||
0x5B};
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
/**
|
||||
* Models a chunk of result data from an RRDatabase.
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class DataChunk {
|
||||
|
||||
private static final String NEWLINE = System.getProperty("line.separator");
|
||||
long startTime;
|
||||
int start;
|
||||
int end;
|
||||
long step;
|
||||
int dsCount;
|
||||
double[][] data;
|
||||
int rows;
|
||||
|
||||
DataChunk(long startTime, int start, int end, long step, int dsCount, int rows) {
|
||||
this.startTime = startTime;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.step = step;
|
||||
this.dsCount = dsCount;
|
||||
this.rows = rows;
|
||||
data = new double[rows][dsCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary of the contents of this data chunk. The first column is
|
||||
* the time (RRD format) and the following columns are the data source
|
||||
* values.
|
||||
*
|
||||
* @return a summary of the contents of this data chunk.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
long time = startTime;
|
||||
|
||||
for (int row = 0; row < rows; row++, time += step) {
|
||||
sb.append(time);
|
||||
sb.append(": ");
|
||||
|
||||
for (int ds = 0; ds < dsCount; ds++) {
|
||||
sb.append(data[row][ds]);
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
sb.append(NEWLINE);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
/**
|
||||
* Instances of this class model a data source in an RRD file.
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class DataSource {
|
||||
|
||||
long offset;
|
||||
long size;
|
||||
String name;
|
||||
DataSourceType type;
|
||||
int minimumHeartbeat;
|
||||
double minimum;
|
||||
double maximum;
|
||||
PDPStatusBlock pdpStatusBlock;
|
||||
|
||||
DataSource(RRDFile file) throws IOException,RrdException {
|
||||
|
||||
offset = file.getFilePointer();
|
||||
name = file.readString(Constants.DS_NAM_SIZE);
|
||||
type = DataSourceType.get(file.readString(Constants.DST_SIZE));
|
||||
|
||||
file.align(8);
|
||||
|
||||
minimumHeartbeat = file.readInt(true);
|
||||
|
||||
file.align(8);
|
||||
|
||||
minimum = file.readDouble();
|
||||
maximum = file.readDouble();
|
||||
|
||||
// Skip rest of ds_def_t.par[]
|
||||
file.align();
|
||||
file.skipBytes(56);
|
||||
|
||||
size = file.getFilePointer() - offset;
|
||||
}
|
||||
|
||||
void loadPDPStatusBlock(RRDFile file) throws IOException,RrdException {
|
||||
pdpStatusBlock = new PDPStatusBlock(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary data point status block for this data source.
|
||||
*
|
||||
* @return the primary data point status block for this data source.
|
||||
*/
|
||||
public PDPStatusBlock getPDPStatusBlock() {
|
||||
return pdpStatusBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum required heartbeat for this data source.
|
||||
*
|
||||
* @return the minimum required heartbeat for this data source.
|
||||
*/
|
||||
public int getMinimumHeartbeat() {
|
||||
return minimumHeartbeat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum value input to this data source can have.
|
||||
*
|
||||
* @return the minimum value input to this data source can have.
|
||||
*/
|
||||
public double getMinimum() {
|
||||
return minimum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type this data source is.
|
||||
*
|
||||
* @return the type this data source is.
|
||||
* @see DataSourceType
|
||||
*/
|
||||
public DataSourceType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum value input to this data source can have.
|
||||
*
|
||||
* @return the maximum value input to this data source can have.
|
||||
*/
|
||||
public double getMaximum() {
|
||||
return maximum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this data source.
|
||||
*
|
||||
* @return the name of this data source.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
void printInfo(PrintStream s, NumberFormat numberFormat) {
|
||||
|
||||
StringBuffer sb = new StringBuffer("ds[");
|
||||
|
||||
sb.append(name);
|
||||
s.print(sb);
|
||||
s.print("].type = \"");
|
||||
s.print(type);
|
||||
s.println("\"");
|
||||
s.print(sb);
|
||||
s.print("].minimal_heartbeat = ");
|
||||
s.println(minimumHeartbeat);
|
||||
s.print(sb);
|
||||
s.print("].min = ");
|
||||
s.println(Double.isNaN(minimum)
|
||||
? "NaN"
|
||||
: numberFormat.format(minimum));
|
||||
s.print(sb);
|
||||
s.print("].max = ");
|
||||
s.println(Double.isNaN(maximum)
|
||||
? "NaN"
|
||||
: numberFormat.format(maximum));
|
||||
s.print(sb);
|
||||
s.print("].last_ds = ");
|
||||
s.println(pdpStatusBlock.lastReading);
|
||||
s.print(sb);
|
||||
s.print("].value = ");
|
||||
|
||||
double value = pdpStatusBlock.value;
|
||||
|
||||
s.println(Double.isNaN(value)
|
||||
? "NaN"
|
||||
: numberFormat.format(value));
|
||||
s.print(sb);
|
||||
s.print("].unknown_sec = ");
|
||||
s.println(pdpStatusBlock.unknownSeconds);
|
||||
}
|
||||
|
||||
void toXml(PrintStream s) {
|
||||
|
||||
s.println("\t<ds>");
|
||||
s.print("\t\t<name> ");
|
||||
s.print(name);
|
||||
s.println(" </name>");
|
||||
s.print("\t\t<type> ");
|
||||
s.print(type);
|
||||
s.println(" </type>");
|
||||
s.print("\t\t<minimal_heartbeat> ");
|
||||
s.print(minimumHeartbeat);
|
||||
s.println(" </minimal_heartbeat>");
|
||||
s.print("\t\t<min> ");
|
||||
s.print(minimum);
|
||||
s.println(" </min>");
|
||||
s.print("\t\t<max> ");
|
||||
s.print(maximum);
|
||||
s.println(" </max>");
|
||||
s.println();
|
||||
s.println("\t\t<!-- PDP Status -->");
|
||||
s.print("\t\t<last_ds> ");
|
||||
s.print(pdpStatusBlock.lastReading);
|
||||
s.println(" </last_ds>");
|
||||
s.print("\t\t<value> ");
|
||||
s.print(pdpStatusBlock.value);
|
||||
s.println(" </value>");
|
||||
s.print("\t\t<unknown_sec> ");
|
||||
s.print(pdpStatusBlock.unknownSeconds);
|
||||
s.println(" </unknown_sec>");
|
||||
s.println("\t</ds>");
|
||||
s.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary the contents of this data source.
|
||||
*
|
||||
* @return a summary of the information contained in this data source.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
StringBuffer sb = new StringBuffer("[DataSource: OFFSET=0x");
|
||||
|
||||
sb.append(Long.toHexString(offset));
|
||||
sb.append(", SIZE=0x");
|
||||
sb.append(Long.toHexString(size));
|
||||
sb.append(", name=");
|
||||
sb.append(name);
|
||||
sb.append(", type=");
|
||||
sb.append(type.toString());
|
||||
sb.append(", minHeartbeat=");
|
||||
sb.append(minimumHeartbeat);
|
||||
sb.append(", min=");
|
||||
sb.append(minimum);
|
||||
sb.append(", max=");
|
||||
sb.append(maximum);
|
||||
sb.append("]");
|
||||
sb.append("\n\t\t");
|
||||
sb.append(pdpStatusBlock.toString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
/**
|
||||
* Class DataSourceType
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class DataSourceType {
|
||||
|
||||
private static final int _COUNTER = 0;
|
||||
private static final String STR_COUNTER = "COUNTER";
|
||||
|
||||
/**
|
||||
* Field COUNTER
|
||||
*/
|
||||
public static final DataSourceType COUNTER = new DataSourceType(_COUNTER);
|
||||
private static final int _ABSOLUTE = 1;
|
||||
private static final String STR_ABSOLUTE = "ABSOLUTE";
|
||||
|
||||
/**
|
||||
* Field ABSOLUTE
|
||||
*/
|
||||
public static final DataSourceType ABSOLUTE = new DataSourceType(_ABSOLUTE);
|
||||
private static final int _GAUGE = 2;
|
||||
private static final String STR_GAUGE = "GAUGE";
|
||||
|
||||
/**
|
||||
* Field GAUGE
|
||||
*/
|
||||
public static final DataSourceType GAUGE = new DataSourceType(_GAUGE);
|
||||
private static final int _DERIVE = 3;
|
||||
private static final String STR_DERIVE = "DERIVE";
|
||||
|
||||
/**
|
||||
* Field DERIVE
|
||||
*/
|
||||
public static final DataSourceType DERIVE = new DataSourceType(_DERIVE);
|
||||
private int type;
|
||||
|
||||
private DataSourceType(final int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <code>DataSourceType</code> with the given name.
|
||||
*
|
||||
* @param s name of the <code>DataSourceType</code> required.
|
||||
* @return a <code>DataSourceType</code> with the given name.
|
||||
*/
|
||||
public static DataSourceType get(final String s) {
|
||||
|
||||
if (STR_COUNTER.equalsIgnoreCase(s)) {
|
||||
return COUNTER;
|
||||
}
|
||||
else if (STR_ABSOLUTE.equalsIgnoreCase(s)) {
|
||||
return ABSOLUTE;
|
||||
}
|
||||
else if (STR_GAUGE.equalsIgnoreCase(s)) {
|
||||
return GAUGE;
|
||||
}
|
||||
else if (STR_DERIVE.equalsIgnoreCase(s)) {
|
||||
return DERIVE;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid DataSourceType");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this object against the specified object.
|
||||
*
|
||||
* @return <code>true</code> if the objects are the same,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean equals(final Object obj) {
|
||||
|
||||
if (!(obj instanceof DataSourceType)) {
|
||||
throw new IllegalArgumentException("Not a DataSourceType");
|
||||
}
|
||||
|
||||
return (((DataSourceType) obj).type == type)
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return type * 37;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this object.
|
||||
*
|
||||
* @return a string representation of this object.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
String strType;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case _COUNTER:
|
||||
strType = STR_COUNTER;
|
||||
break;
|
||||
|
||||
case _ABSOLUTE:
|
||||
strType = STR_ABSOLUTE;
|
||||
break;
|
||||
|
||||
case _GAUGE:
|
||||
strType = STR_GAUGE;
|
||||
break;
|
||||
|
||||
case _DERIVE:
|
||||
strType = STR_DERIVE;
|
||||
break;
|
||||
|
||||
default :
|
||||
// Don't you just hate it when you see a line like this?
|
||||
throw new RuntimeException("This should never happen");
|
||||
}
|
||||
|
||||
return strType;
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
/**
|
||||
* Instances of this class model the header section of an RRD file.
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class Header implements Constants {
|
||||
|
||||
static final long offset = 0;
|
||||
long size;
|
||||
String version;
|
||||
int intVersion;
|
||||
int dsCount;
|
||||
int rraCount;
|
||||
int pdpStep;
|
||||
|
||||
Header(RRDFile file) throws IOException,RrdException {
|
||||
|
||||
if (!file.readString(4).equals(COOKIE)) {
|
||||
throw new IOException("Invalid COOKIE");
|
||||
}
|
||||
|
||||
version = file.readString(5);
|
||||
intVersion = Integer.parseInt(version);
|
||||
if( intVersion > 3 ) {
|
||||
throw new IOException("Unsupported RRD version (" + version + ")");
|
||||
}
|
||||
|
||||
file.align();
|
||||
|
||||
// Consume the FLOAT_COOKIE
|
||||
file.readDouble();
|
||||
|
||||
dsCount = file.readInt();
|
||||
rraCount = file.readInt();
|
||||
pdpStep = file.readInt();
|
||||
|
||||
// Skip rest of stat_head_t.par
|
||||
file.align();
|
||||
file.skipBytes(80);
|
||||
|
||||
size = file.getFilePointer() - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the database.
|
||||
*
|
||||
* @return the version of the database.
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getIntVersion() {
|
||||
return intVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of <code>DataSource</code>s in the database.
|
||||
*
|
||||
* @return the number of <code>DataSource</code>s in the database.
|
||||
*/
|
||||
public int getDSCount() {
|
||||
return dsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of <code>Archive</code>s in the database.
|
||||
*
|
||||
* @return the number of <code>Archive</code>s in the database.
|
||||
*/
|
||||
public int getRRACount() {
|
||||
return rraCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary data point interval in seconds.
|
||||
*
|
||||
* @return the primary data point interval in seconds.
|
||||
*/
|
||||
public int getPDPStep() {
|
||||
return pdpStep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary the contents of this header.
|
||||
*
|
||||
* @return a summary of the information contained in this header.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
StringBuffer sb = new StringBuffer("[Header: OFFSET=0x00, SIZE=0x");
|
||||
|
||||
sb.append(Long.toHexString(size));
|
||||
sb.append(", version=");
|
||||
sb.append(version);
|
||||
sb.append(", dsCount=");
|
||||
sb.append(dsCount);
|
||||
sb.append(", rraCount=");
|
||||
sb.append(rraCount);
|
||||
sb.append(", pdpStep=");
|
||||
sb.append(pdpStep);
|
||||
sb.append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
/**
|
||||
* Show some of the things jRRD can do.
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public Main(String rrdFile) {
|
||||
|
||||
RRDatabase rrd = null;
|
||||
DataChunk chunk = null;
|
||||
|
||||
try {
|
||||
rrd = new RRDatabase(rrdFile);
|
||||
chunk = rrd.getData(ConsolidationFunctionType.AVERAGE);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
rrd.toXml(System.out);
|
||||
} catch (RrdException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
// Dump the database as XML.
|
||||
rrd.printInfo(System.out); // Dump the database header information.
|
||||
System.out.println(rrd); // Dump a summary of the contents of the database.
|
||||
System.out.println(chunk); // Dump the chunk.
|
||||
|
||||
try {
|
||||
rrd.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(int status) {
|
||||
System.err.println("Usage: " + Main.class.getName() + " rrdfile");
|
||||
System.exit(status);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 1) {
|
||||
usage(1);
|
||||
}
|
||||
new Main(args[0]);
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
/**
|
||||
* Instances of this class model the primary data point status from an RRD file.
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class PDPStatusBlock {
|
||||
|
||||
long offset;
|
||||
long size;
|
||||
String lastReading;
|
||||
int unknownSeconds;
|
||||
double value;
|
||||
|
||||
PDPStatusBlock(RRDFile file) throws IOException,RrdException {
|
||||
|
||||
offset = file.getFilePointer();
|
||||
lastReading = file.readString(Constants.LAST_DS_LEN);
|
||||
|
||||
file.align(4);
|
||||
|
||||
unknownSeconds = file.readInt();
|
||||
|
||||
file.align(8); //8 bytes per scratch value in pdp_prep; align on that
|
||||
|
||||
value = file.readDouble();
|
||||
|
||||
// Skip rest of pdp_prep_t.par[]
|
||||
file.skipBytes(64);
|
||||
|
||||
size = file.getFilePointer() - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last reading from the data source.
|
||||
*
|
||||
* @return the last reading from the data source.
|
||||
*/
|
||||
public String getLastReading() {
|
||||
return lastReading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the primary data point.
|
||||
*
|
||||
* @return the current value of the primary data point.
|
||||
*/
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of seconds of the current primary data point is
|
||||
* unknown data.
|
||||
*
|
||||
* @return the number of seconds of the current primary data point is unknown data.
|
||||
*/
|
||||
public int getUnknownSeconds() {
|
||||
return unknownSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary the contents of this PDP status block.
|
||||
*
|
||||
* @return a summary of the information contained in this PDP status block.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
StringBuffer sb = new StringBuffer("[PDPStatus: OFFSET=0x");
|
||||
|
||||
sb.append(Long.toHexString(offset));
|
||||
sb.append(", SIZE=0x");
|
||||
sb.append(Long.toHexString(size));
|
||||
sb.append(", lastReading=");
|
||||
sb.append(lastReading);
|
||||
sb.append(", unknownSeconds=");
|
||||
sb.append(unknownSeconds);
|
||||
sb.append(", value=");
|
||||
sb.append(value);
|
||||
sb.append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
/**
|
||||
* This class is a quick hack to read information from an RRD file. Writing
|
||||
* to RRD files is not currently supported. As I said, this is a quick hack.
|
||||
* Some thought should be put into the overall design of the file IO.
|
||||
* <p>
|
||||
* Currently this can read RRD files that were generated on Solaris (Sparc)
|
||||
* and Linux (x86).
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class RRDFile implements Constants {
|
||||
|
||||
boolean bigEndian;
|
||||
boolean debug;
|
||||
int alignment;
|
||||
RandomAccessFile ras;
|
||||
byte[] buffer;
|
||||
|
||||
RRDFile(String name) throws IOException, RrdException {
|
||||
this(new File(name));
|
||||
}
|
||||
|
||||
RRDFile(File file) throws IOException, RrdException {
|
||||
|
||||
ras = new RandomAccessFile(file, "r");
|
||||
buffer = new byte[128];
|
||||
|
||||
this.debug = false;
|
||||
initDataLayout(file);
|
||||
}
|
||||
|
||||
private void initDataLayout(File file) throws IOException, RrdException {
|
||||
|
||||
if (file.exists()) { // Load the data formats from the file
|
||||
int bytes = ras.read(buffer, 0, 24);
|
||||
if (bytes < 24) {
|
||||
throw new RrdException("Invalid RRD file");
|
||||
}
|
||||
|
||||
int index;
|
||||
|
||||
if ((index = indexOf(FLOAT_COOKIE_BIG_ENDIAN, buffer)) != -1) {
|
||||
bigEndian = true;
|
||||
}
|
||||
else if ((index = indexOf(FLOAT_COOKIE_LITTLE_ENDIAN, buffer))
|
||||
!= -1) {
|
||||
bigEndian = false;
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Invalid RRD file");
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
|
||||
case 12:
|
||||
alignment = 4;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
alignment = 8;
|
||||
break;
|
||||
|
||||
default :
|
||||
throw new RuntimeException("Unsupported architecture - neither 32-bit nor 64-bit, or maybe the file is corrupt");
|
||||
}
|
||||
}
|
||||
else { // Default to data formats for this hardware architecture
|
||||
}
|
||||
|
||||
ras.seek(0); // Reset file pointer to start of file
|
||||
}
|
||||
|
||||
private int indexOf(byte[] pattern, byte[] array) {
|
||||
return (new String(array)).indexOf(new String(pattern));
|
||||
}
|
||||
|
||||
boolean isBigEndian() {
|
||||
return bigEndian;
|
||||
}
|
||||
|
||||
int getAlignment() {
|
||||
return alignment;
|
||||
}
|
||||
|
||||
double readDouble() throws IOException, RrdException {
|
||||
if(debug) {
|
||||
System.out.print("Read 8 bytes (Double) from offset "+ras.getFilePointer()+":");
|
||||
}
|
||||
|
||||
//double value;
|
||||
byte[] tx = new byte[8];
|
||||
|
||||
if(ras.read(buffer, 0, 8) != 8) {
|
||||
throw new RrdException("Invalid RRD file");
|
||||
}
|
||||
|
||||
if (bigEndian) {
|
||||
tx = buffer;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
tx[7 - i] = buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
DataInputStream reverseDis =
|
||||
new DataInputStream(new ByteArrayInputStream(tx));
|
||||
|
||||
Double result = reverseDis.readDouble();
|
||||
if(this.debug) {
|
||||
System.out.println(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int readInt() throws IOException, RrdException {
|
||||
return readInt(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next integer (4 or 8 bytes depending on alignment), advancing the file pointer
|
||||
* and returns it
|
||||
* If the alignment is 8-bytes (64-bit), then 8 bytes are read, but only the lower 4-bytes (32-bits) are
|
||||
* returned. The upper 4 bytes are ignored.
|
||||
*
|
||||
* @return the 32-bit integer read from the file
|
||||
* @throws IOException - A file access error
|
||||
* @throws RrdException - Not enough bytes were left in the file to read the integer.
|
||||
*/
|
||||
int readInt(boolean dump) throws IOException, RrdException {
|
||||
//An integer is "alignment" bytes long - 4 bytes on 32-bit, 8 on 64-bit.
|
||||
if(this.debug) {
|
||||
System.out.print("Read "+alignment+" bytes (int) from offset "+ras.getFilePointer()+":");
|
||||
}
|
||||
|
||||
if(ras.read(buffer, 0, alignment) != alignment) {
|
||||
throw new RrdException("Invalid RRD file");
|
||||
}
|
||||
|
||||
int value;
|
||||
|
||||
if (bigEndian) {
|
||||
if(alignment == 8) {
|
||||
//For big-endian, the low 4-bytes of the 64-bit integer are the last 4 bytes
|
||||
value = (0xFF & buffer[7]) | ((0xFF & buffer[6]) << 8)
|
||||
| ((0xFF & buffer[5]) << 16) | ((0xFF & buffer[4]) << 24);
|
||||
} else {
|
||||
value = (0xFF & buffer[3]) | ((0xFF & buffer[2]) << 8)
|
||||
| ((0xFF & buffer[1]) << 16) | ((0xFF & buffer[0]) << 24);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//For little-endian, there's no difference between 4 and 8 byte alignment.
|
||||
// The first 4 bytes are the low end of a 64-bit number
|
||||
value = (0xFF & buffer[0]) | ((0xFF & buffer[1]) << 8)
|
||||
| ((0xFF & buffer[2]) << 16) | ((0xFF & buffer[3]) << 24);
|
||||
}
|
||||
|
||||
if(this.debug) {
|
||||
System.out.println(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
String readString(int maxLength) throws IOException, RrdException {
|
||||
if(this.debug) {
|
||||
System.out.print("Read "+maxLength+" bytes (string) from offset "+ras.getFilePointer()+":");
|
||||
}
|
||||
maxLength = ras.read(buffer, 0, maxLength);
|
||||
if(maxLength == -1) {
|
||||
throw new RrdException("Invalid RRD file");
|
||||
}
|
||||
|
||||
String result = new String(buffer, 0, maxLength).trim();
|
||||
if(this.debug) {
|
||||
System.out.println( result +":");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void skipBytes(final int n) throws IOException {
|
||||
int bytesSkipped = ras.skipBytes(n);
|
||||
if(this.debug) {
|
||||
System.out.println("Skipping "+bytesSkipped+" bytes");
|
||||
}
|
||||
}
|
||||
|
||||
int align(int boundary) throws IOException {
|
||||
|
||||
int skip = (int) (boundary - (ras.getFilePointer() % boundary)) % boundary;
|
||||
|
||||
if (skip != 0) {
|
||||
skip = ras.skipBytes(skip);
|
||||
}
|
||||
if(this.debug) {
|
||||
System.out.println("Aligning to boundary "+ boundary +". Offset is now "+ras.getFilePointer());
|
||||
}
|
||||
return skip;
|
||||
}
|
||||
|
||||
int align() throws IOException {
|
||||
return align(alignment);
|
||||
}
|
||||
|
||||
long info() throws IOException {
|
||||
return ras.getFilePointer();
|
||||
}
|
||||
|
||||
long getFilePointer() throws IOException {
|
||||
return ras.getFilePointer();
|
||||
}
|
||||
|
||||
void close() throws IOException {
|
||||
ras.close();
|
||||
}
|
||||
}
|
||||
@@ -1,508 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.jrrd;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
/**
|
||||
* Instances of this class model
|
||||
* <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/">Round Robin Database</a>
|
||||
* (RRD) files.
|
||||
*
|
||||
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class RRDatabase {
|
||||
|
||||
RRDFile rrdFile;
|
||||
|
||||
// RRD file name
|
||||
private String name;
|
||||
Header header;
|
||||
ArrayList<DataSource> dataSources;
|
||||
ArrayList<Archive> archives;
|
||||
Date lastUpdate;
|
||||
|
||||
/**
|
||||
* Creates a database to read from.
|
||||
*
|
||||
* @param name the filename of the file to read from.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public RRDatabase(String name) throws IOException,RrdException {
|
||||
this(new File(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a database to read from.
|
||||
*
|
||||
* @param file the file to read from.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public RRDatabase(File file) throws IOException,RrdException {
|
||||
|
||||
name = file.getName();
|
||||
rrdFile = new RRDFile(file);
|
||||
header = new Header(rrdFile);
|
||||
|
||||
// Load the data sources
|
||||
dataSources = new ArrayList<DataSource>();
|
||||
|
||||
for (int i = 0; i < header.dsCount; i++) {
|
||||
DataSource ds = new DataSource(rrdFile);
|
||||
|
||||
dataSources.add(ds);
|
||||
}
|
||||
|
||||
// Load the archives
|
||||
archives = new ArrayList<Archive>();
|
||||
|
||||
for (int i = 0; i < header.rraCount; i++) {
|
||||
Archive archive = new Archive(this);
|
||||
|
||||
archives.add(archive);
|
||||
}
|
||||
|
||||
rrdFile.align();
|
||||
|
||||
long timestamp = (long)(rrdFile.readInt()) * 1000;
|
||||
if(header.getIntVersion() >= 3) {
|
||||
//Version 3 has an additional microsecond field
|
||||
int microSeconds = rrdFile.readInt();
|
||||
timestamp += (microSeconds/1000); //Date only does up to milliseconds
|
||||
}
|
||||
lastUpdate = new Date( timestamp );
|
||||
// Load PDPStatus(s)
|
||||
for (int i = 0; i < header.dsCount; i++) {
|
||||
DataSource ds = dataSources.get(i);
|
||||
|
||||
ds.loadPDPStatusBlock(rrdFile);
|
||||
}
|
||||
|
||||
// Load CDPStatus(s)
|
||||
for (int i = 0; i < header.rraCount; i++) {
|
||||
Archive archive = archives.get(i);
|
||||
|
||||
archive.loadCDPStatusBlocks(rrdFile, header.dsCount);
|
||||
}
|
||||
|
||||
// Load current row information for each archive
|
||||
for (int i = 0; i < header.rraCount; i++) {
|
||||
Archive archive = archives.get(i);
|
||||
|
||||
archive.loadCurrentRow(rrdFile);
|
||||
}
|
||||
|
||||
// Now load the data
|
||||
for (int i = 0; i < header.rraCount; i++) {
|
||||
Archive archive = archives.get(i);
|
||||
|
||||
archive.loadData(rrdFile, header.dsCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>Header</code> for this database.
|
||||
*
|
||||
* @return the <code>Header</code> for this database.
|
||||
*/
|
||||
public Header getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date this database was last updated. To convert this date to
|
||||
* the form returned by <code>rrdtool last</code> call Date.getTime() and
|
||||
* divide the result by 1000.
|
||||
*
|
||||
* @return the date this database was last updated.
|
||||
*/
|
||||
public Date getLastUpdate() {
|
||||
return lastUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>DataSource</code> at the specified position in this database.
|
||||
*
|
||||
* @param index index of <code>DataSource</code> to return.
|
||||
* @return the <code>DataSource</code> at the specified position in this database
|
||||
*/
|
||||
public DataSource getDataSource(int index) {
|
||||
return dataSources.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the data sources in this database in proper sequence.
|
||||
*
|
||||
* @return an iterator over the data sources in this database in proper sequence.
|
||||
*/
|
||||
public Iterator<DataSource> getDataSources() {
|
||||
return dataSources.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>Archive</code> at the specified position in this database.
|
||||
*
|
||||
* @param index index of <code>Archive</code> to return.
|
||||
* @return the <code>Archive</code> at the specified position in this database.
|
||||
*/
|
||||
public Archive getArchive(int index) {
|
||||
return archives.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the archives in this database in proper sequence.
|
||||
*
|
||||
* @return an iterator over the archives in this database in proper sequence.
|
||||
*/
|
||||
public Iterator<Archive> getArchives() {
|
||||
return archives.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of archives in this database.
|
||||
*
|
||||
* @return the number of archives in this database.
|
||||
*/
|
||||
public int getNumArchives() {
|
||||
return header.rraCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the archives in this database of the given type
|
||||
* in proper sequence.
|
||||
*
|
||||
* @param type the consolidation function that should have been applied to
|
||||
* the data.
|
||||
* @return an iterator over the archives in this database of the given type
|
||||
* in proper sequence.
|
||||
*/
|
||||
public Iterator<Archive> getArchives(ConsolidationFunctionType type) {
|
||||
return getArchiveList(type).iterator();
|
||||
}
|
||||
|
||||
ArrayList<Archive> getArchiveList(ConsolidationFunctionType type) {
|
||||
|
||||
ArrayList<Archive> subset = new ArrayList<Archive>();
|
||||
|
||||
for (int i = 0; i < archives.size(); i++) {
|
||||
Archive archive = archives.get(i);
|
||||
|
||||
if (archive.getType().equals(type)) {
|
||||
subset.add(archive);
|
||||
}
|
||||
}
|
||||
|
||||
return subset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this database stream and releases any associated system resources.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
rrdFile.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the header information of the database to the given print stream
|
||||
* using the default number format. The default format for <code>double</code>
|
||||
* is 0.0000000000E0.
|
||||
*
|
||||
* @param s the PrintStream to print the header information to.
|
||||
*/
|
||||
public void printInfo(PrintStream s) {
|
||||
|
||||
NumberFormat numberFormat = new DecimalFormat("0.0000000000E0");
|
||||
|
||||
printInfo(s, numberFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data from the database corresponding to the given consolidation
|
||||
* function and a step size of 1.
|
||||
*
|
||||
* @param type the consolidation function that should have been applied to
|
||||
* the data.
|
||||
* @return the raw data.
|
||||
* @throws RrdException if there was a problem locating a data archive with
|
||||
* the requested consolidation function.
|
||||
* @throws IOException if there was a problem reading data from the database.
|
||||
*/
|
||||
public DataChunk getData(ConsolidationFunctionType type)
|
||||
throws RrdException, IOException {
|
||||
return getData(type, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data from the database corresponding to the given consolidation
|
||||
* function.
|
||||
*
|
||||
* @param type the consolidation function that should have been applied to
|
||||
* the data.
|
||||
* @param step the step size to use.
|
||||
* @return the raw data.
|
||||
* @throws RrdException if there was a problem locating a data archive with
|
||||
* the requested consolidation function.
|
||||
* @throws IOException if there was a problem reading data from the database.
|
||||
*/
|
||||
public DataChunk getData(ConsolidationFunctionType type, long step)
|
||||
throws RrdException, IOException {
|
||||
|
||||
ArrayList<Archive> possibleArchives = getArchiveList(type);
|
||||
|
||||
if (possibleArchives.size() == 0) {
|
||||
throw new RrdException("Database does not contain an Archive of consolidation function type "
|
||||
+ type);
|
||||
}
|
||||
|
||||
Calendar endCal = Calendar.getInstance();
|
||||
|
||||
endCal.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
Calendar startCal = (Calendar) endCal.clone();
|
||||
|
||||
startCal.add(Calendar.DATE, -1);
|
||||
|
||||
long end = endCal.getTime().getTime() / 1000;
|
||||
long start = startCal.getTime().getTime() / 1000;
|
||||
Archive archive = findBestArchive(start, end, step, possibleArchives);
|
||||
|
||||
// Tune the parameters
|
||||
step = header.pdpStep * archive.pdpCount;
|
||||
start -= start % step;
|
||||
|
||||
if (end % step != 0) {
|
||||
end += step - end % step;
|
||||
}
|
||||
|
||||
int rows = (int) ((end - start) / step + 1);
|
||||
|
||||
//cat.debug("start " + start + " end " + end + " step " + step + " rows "
|
||||
// + rows);
|
||||
|
||||
// Find start and end offsets
|
||||
// This is terrible - some of this should be encapsulated in Archive - CT.
|
||||
long lastUpdateLong = lastUpdate.getTime() / 1000;
|
||||
long archiveEndTime = lastUpdateLong - (lastUpdateLong % step);
|
||||
long archiveStartTime = archiveEndTime - (step * (archive.rowCount - 1));
|
||||
int startOffset = (int) ((start - archiveStartTime) / step);
|
||||
int endOffset = (int) ((archiveEndTime - end) / step);
|
||||
|
||||
//cat.debug("start " + archiveStartTime + " end " + archiveEndTime
|
||||
// + " startOffset " + startOffset + " endOffset "
|
||||
// + (archive.rowCount - endOffset));
|
||||
|
||||
DataChunk chunk = new DataChunk(start, startOffset, endOffset, step,
|
||||
header.dsCount, rows);
|
||||
|
||||
archive.loadData(chunk);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is almost a verbatim copy of the original C code by Tobias Oetiker.
|
||||
* I need to put more of a Java style on it - CT
|
||||
*/
|
||||
private Archive findBestArchive(long start, long end, long step,
|
||||
ArrayList<Archive> archives) {
|
||||
|
||||
Archive archive = null;
|
||||
Archive bestFullArchive = null;
|
||||
Archive bestPartialArchive = null;
|
||||
long lastUpdateLong = lastUpdate.getTime() / 1000;
|
||||
int firstPart = 1;
|
||||
int firstFull = 1;
|
||||
long bestMatch = 0;
|
||||
//long bestPartRRA = 0;
|
||||
long bestStepDiff = 0;
|
||||
long tmpStepDiff = 0;
|
||||
|
||||
for (int i = 0; i < archives.size(); i++) {
|
||||
archive = archives.get(i);
|
||||
|
||||
long calEnd = lastUpdateLong
|
||||
- (lastUpdateLong
|
||||
% (archive.pdpCount * header.pdpStep));
|
||||
long calStart = calEnd
|
||||
- (archive.pdpCount * archive.rowCount
|
||||
* header.pdpStep);
|
||||
long fullMatch = end - start;
|
||||
|
||||
if ((calEnd >= end) && (calStart < start)) { // Best full match
|
||||
tmpStepDiff = Math.abs(step - (header.pdpStep * archive.pdpCount));
|
||||
|
||||
if ((firstFull != 0) || (tmpStepDiff < bestStepDiff)) {
|
||||
firstFull = 0;
|
||||
bestStepDiff = tmpStepDiff;
|
||||
bestFullArchive = archive;
|
||||
}
|
||||
}
|
||||
else { // Best partial match
|
||||
long tmpMatch = fullMatch;
|
||||
|
||||
if (calStart > start) {
|
||||
tmpMatch -= calStart - start;
|
||||
}
|
||||
|
||||
if (calEnd < end) {
|
||||
tmpMatch -= end - calEnd;
|
||||
}
|
||||
|
||||
if ((firstPart != 0) || (bestMatch < tmpMatch)) {
|
||||
firstPart = 0;
|
||||
bestMatch = tmpMatch;
|
||||
bestPartialArchive = archive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See how the matching went
|
||||
// optimise this
|
||||
if (firstFull == 0) {
|
||||
archive = bestFullArchive;
|
||||
}
|
||||
else if (firstPart == 0) {
|
||||
archive = bestPartialArchive;
|
||||
}
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the header information of the database to the given print stream
|
||||
* using the given number format. The format is almost identical to that
|
||||
* produced by
|
||||
* <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/manual/rrdinfo.html">rrdtool info</a>
|
||||
*
|
||||
* @param s the PrintStream to print the header information to.
|
||||
* @param numberFormat the format to print <code>double</code>s as.
|
||||
*/
|
||||
public void printInfo(PrintStream s, NumberFormat numberFormat) {
|
||||
|
||||
s.print("filename = \"");
|
||||
s.print(name);
|
||||
s.println("\"");
|
||||
s.print("rrd_version = \"");
|
||||
s.print(header.version);
|
||||
s.println("\"");
|
||||
s.print("step = ");
|
||||
s.println(header.pdpStep);
|
||||
s.print("last_update = ");
|
||||
s.println(lastUpdate.getTime() / 1000);
|
||||
|
||||
for (Iterator<DataSource> i = dataSources.iterator(); i.hasNext();) {
|
||||
DataSource ds = i.next();
|
||||
|
||||
ds.printInfo(s, numberFormat);
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (Iterator<Archive> i = archives.iterator(); i.hasNext();) {
|
||||
Archive archive = i.next();
|
||||
|
||||
archive.printInfo(s, numberFormat, index++);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the content of the database to the given print stream
|
||||
* as a stream of XML. The XML format is almost identical to that produced by
|
||||
* <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/manual/rrddump.html">rrdtool dump</a>
|
||||
*
|
||||
* @param s the PrintStream to send the XML to.
|
||||
*/
|
||||
public void toXml(PrintStream s) throws RrdException {
|
||||
|
||||
s.println("<!--");
|
||||
s.println(" -- Round Robin RRDatabase Dump ");
|
||||
s.println(" -- Generated by jRRD <ciaran@codeloop.com>");
|
||||
s.println(" -->");
|
||||
s.println("<rrd>");
|
||||
s.print("\t<version> ");
|
||||
s.print(header.version);
|
||||
s.println(" </version>");
|
||||
s.print("\t<step> ");
|
||||
s.print(header.pdpStep);
|
||||
s.println(" </step> <!-- Seconds -->");
|
||||
s.print("\t<lastupdate> ");
|
||||
s.print(lastUpdate.getTime() / 1000);
|
||||
s.print(" </lastupdate> <!-- ");
|
||||
s.print(lastUpdate.toString());
|
||||
s.println(" -->");
|
||||
s.println();
|
||||
|
||||
for (int i = 0; i < header.dsCount; i++) {
|
||||
DataSource ds = dataSources.get(i);
|
||||
|
||||
ds.toXml(s);
|
||||
}
|
||||
|
||||
s.println("<!-- Round Robin Archives -->");
|
||||
|
||||
for (int i = 0; i < header.rraCount; i++) {
|
||||
Archive archive = archives.get(i);
|
||||
|
||||
archive.toXml(s);
|
||||
}
|
||||
|
||||
s.println("</rrd>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary the contents of this database.
|
||||
*
|
||||
* @return a summary of the information contained in this database.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
StringBuffer sb = new StringBuffer("\n");
|
||||
|
||||
sb.append(header.toString());
|
||||
|
||||
for (Iterator<DataSource> i = dataSources.iterator(); i.hasNext();) {
|
||||
DataSource ds = i.next();
|
||||
|
||||
sb.append("\n\t");
|
||||
sb.append(ds.toString());
|
||||
}
|
||||
|
||||
for (Iterator<Archive> i = archives.iterator(); i.hasNext();) {
|
||||
Archive archive = i.next();
|
||||
|
||||
sb.append("\n\t");
|
||||
sb.append(archive.toString());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.timespec;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Small swing-based utility to convert timestamps (seconds since epoch) to readable dates and vice versa.
|
||||
* Supports at-style time specification (like "now-2d", "noon yesterday") and other human-readable
|
||||
* data formats:<p>
|
||||
* <ul>
|
||||
* <li>MM/dd/yy HH:mm:ss
|
||||
* <li>dd.MM.yy HH:mm:ss
|
||||
* <li>dd.MM.yy HH:mm:ss
|
||||
* <li>MM/dd/yy HH:mm
|
||||
* <li>dd.MM.yy HH:mm
|
||||
* <li>yy-MM-dd HH:mm
|
||||
* <li>MM/dd/yy
|
||||
* <li>dd.MM.yy
|
||||
* <li>yy-MM-dd
|
||||
* <li>HH:mm MM/dd/yy
|
||||
* <li>HH:mm dd.MM.yy
|
||||
* <li>HH:mm yy-MM-dd
|
||||
* <li>HH:mm:ss MM/dd/yy
|
||||
* <li>HH:mm:ss dd.MM.yy
|
||||
* <li>HH:mm:ss yy-MM-dd
|
||||
* </ul>
|
||||
* The current timestamp is displayed in the title bar :)<p>
|
||||
*/
|
||||
public class Epoch extends JFrame {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String[] supportedFormats = {
|
||||
"MM/dd/yy HH:mm:ss", "dd.MM.yy HH:mm:ss", "yy-MM-dd HH:mm:ss", "MM/dd/yy HH:mm",
|
||||
"dd.MM.yy HH:mm", "yy-MM-dd HH:mm", "MM/dd/yy", "dd.MM.yy", "yy-MM-dd", "HH:mm MM/dd/yy",
|
||||
"HH:mm dd.MM.yy", "HH:mm yy-MM-dd", "HH:mm:ss MM/dd/yy", "HH:mm:ss dd.MM.yy", "HH:mm:ss yy-MM-dd"
|
||||
};
|
||||
|
||||
private static final SimpleDateFormat[] parsers = new SimpleDateFormat[supportedFormats.length];
|
||||
private static final String helpText;
|
||||
|
||||
private Timer timer = new Timer(1000, new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
showTimestamp();
|
||||
}
|
||||
});
|
||||
|
||||
static {
|
||||
for (int i = 0; i < parsers.length; i++) {
|
||||
parsers[i] = new SimpleDateFormat(supportedFormats[i]);
|
||||
parsers[i].setLenient(true);
|
||||
}
|
||||
StringBuffer tooltipBuff = new StringBuffer("<html><b>Supported input formats:</b><br>");
|
||||
for (String supportedFormat : supportedFormats) {
|
||||
tooltipBuff.append(supportedFormat).append("<br>");
|
||||
}
|
||||
tooltipBuff.append("<b>AT-style time specification</b><br>");
|
||||
tooltipBuff.append("timestamp<br><br>");
|
||||
tooltipBuff.append("Copyright (C) 2003-2005 Sasa Markovic, All Rights Reserved</html>");
|
||||
helpText = tooltipBuff.toString();
|
||||
}
|
||||
|
||||
private JLabel topLabel = new JLabel("Enter timestamp or readable date:");
|
||||
private JTextField inputField = new JTextField(25);
|
||||
private JButton convertButton = new JButton("Convert");
|
||||
private JButton helpButton = new JButton("Help");
|
||||
|
||||
private static final SimpleDateFormat OUTPUT_DATE_FORMAT =
|
||||
new SimpleDateFormat("MM/dd/yy HH:mm:ss EEE");
|
||||
|
||||
Epoch() {
|
||||
super("Epoch");
|
||||
constructUI();
|
||||
timer.start();
|
||||
}
|
||||
|
||||
private void constructUI() {
|
||||
JPanel c = (JPanel) getContentPane();
|
||||
c.setLayout(new BorderLayout(3, 3));
|
||||
c.add(topLabel, BorderLayout.NORTH);
|
||||
c.add(inputField, BorderLayout.WEST);
|
||||
c.add(convertButton, BorderLayout.CENTER);
|
||||
convertButton.setToolTipText(helpText);
|
||||
convertButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
convert();
|
||||
}
|
||||
});
|
||||
c.add(helpButton, BorderLayout.EAST);
|
||||
helpButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JOptionPane.showMessageDialog(helpButton, helpText, "Epoch Help", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
});
|
||||
inputField.requestFocus();
|
||||
getRootPane().setDefaultButton(convertButton);
|
||||
setResizable(false);
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
pack();
|
||||
centerOnScreen();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
void centerOnScreen() {
|
||||
Toolkit t = Toolkit.getDefaultToolkit();
|
||||
Dimension screenSize = t.getScreenSize();
|
||||
Dimension frameSize = getPreferredSize();
|
||||
double x = (screenSize.getWidth() - frameSize.getWidth()) / 2;
|
||||
double y = (screenSize.getHeight() - frameSize.getHeight()) / 2;
|
||||
setLocation((int) x, (int) y);
|
||||
}
|
||||
|
||||
private void convert() {
|
||||
String time = inputField.getText().trim();
|
||||
if (time.length() > 0) {
|
||||
// try simple timestamp
|
||||
try {
|
||||
long timestamp = Long.parseLong(time);
|
||||
Date date = new Date(timestamp * 1000L);
|
||||
formatDate(date);
|
||||
}
|
||||
catch (NumberFormatException nfe) {
|
||||
// failed, try as a date
|
||||
try {
|
||||
inputField.setText("" + parseDate(time));
|
||||
}
|
||||
catch (RrdException e) {
|
||||
inputField.setText("Could not convert, sorry");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showTimestamp() {
|
||||
long timestamp = Util.getTime();
|
||||
setTitle(timestamp + " seconds since epoch");
|
||||
}
|
||||
|
||||
void formatDate(Date date) {
|
||||
inputField.setText(OUTPUT_DATE_FORMAT.format(date));
|
||||
}
|
||||
|
||||
private long parseDate(String time) throws RrdException {
|
||||
for (SimpleDateFormat parser : parsers) {
|
||||
try {
|
||||
return Util.getTimestamp(parser.parse(time));
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
return new TimeParser(time).parse().getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method which runs this utility.
|
||||
*
|
||||
* @param args Not used.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
new Epoch();
|
||||
}
|
||||
}
|
||||
@@ -1,443 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
* Java port of Tobi's original parsetime.c routine
|
||||
*/
|
||||
package org.jrobin.core.timespec;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
/**
|
||||
* Class which parses at-style time specification (described in detail on the rrdfetch man page),
|
||||
* used in all RRDTool commands. This code is in most parts just a java port of Tobi's parsetime.c
|
||||
* code.
|
||||
*/
|
||||
public class TimeParser {
|
||||
private static final int PREVIOUS_OP = -1;
|
||||
|
||||
TimeToken token;
|
||||
TimeScanner scanner;
|
||||
TimeSpec spec;
|
||||
|
||||
int op = TimeToken.PLUS;
|
||||
int prev_multiplier = -1;
|
||||
|
||||
/**
|
||||
* Constructs TimeParser instance from the given input string.
|
||||
*
|
||||
* @param dateString at-style time specification (read rrdfetch man page
|
||||
* for the complete explanation)
|
||||
*/
|
||||
public TimeParser(String dateString) {
|
||||
scanner = new TimeScanner(dateString);
|
||||
spec = new TimeSpec(dateString);
|
||||
}
|
||||
|
||||
private void expectToken(int desired, String errorMessage) throws RrdException {
|
||||
token = scanner.nextToken();
|
||||
if (token.id != desired) {
|
||||
throw new RrdException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void plusMinus(int doop) throws RrdException {
|
||||
if (doop >= 0) {
|
||||
op = doop;
|
||||
expectToken(TimeToken.NUMBER, "There should be number after " +
|
||||
(op == TimeToken.PLUS ? '+' : '-'));
|
||||
prev_multiplier = -1; /* reset months-minutes guessing mechanics */
|
||||
}
|
||||
int delta = Integer.parseInt(token.value);
|
||||
token = scanner.nextToken();
|
||||
if (token.id == TimeToken.MONTHS_MINUTES) {
|
||||
/* hard job to guess what does that -5m means: -5mon or -5min? */
|
||||
switch (prev_multiplier) {
|
||||
case TimeToken.DAYS:
|
||||
case TimeToken.WEEKS:
|
||||
case TimeToken.MONTHS:
|
||||
case TimeToken.YEARS:
|
||||
token = scanner.resolveMonthsMinutes(TimeToken.MONTHS);
|
||||
break;
|
||||
case TimeToken.SECONDS:
|
||||
case TimeToken.MINUTES:
|
||||
case TimeToken.HOURS:
|
||||
token = scanner.resolveMonthsMinutes(TimeToken.MINUTES);
|
||||
break;
|
||||
default:
|
||||
if (delta < 6) {
|
||||
token = scanner.resolveMonthsMinutes(TimeToken.MONTHS);
|
||||
}
|
||||
else {
|
||||
token = scanner.resolveMonthsMinutes(TimeToken.MINUTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
prev_multiplier = token.id;
|
||||
delta *= (op == TimeToken.PLUS) ? +1 : -1;
|
||||
switch (token.id) {
|
||||
case TimeToken.YEARS:
|
||||
spec.dyear += delta;
|
||||
break;
|
||||
case TimeToken.MONTHS:
|
||||
spec.dmonth += delta;
|
||||
break;
|
||||
case TimeToken.WEEKS:
|
||||
delta *= 7;
|
||||
spec.dday += delta;
|
||||
break;
|
||||
case TimeToken.DAYS:
|
||||
spec.dday += delta;
|
||||
break;
|
||||
case TimeToken.HOURS:
|
||||
spec.dhour += delta;
|
||||
break;
|
||||
case TimeToken.MINUTES:
|
||||
spec.dmin += delta;
|
||||
break;
|
||||
case TimeToken.SECONDS:
|
||||
default: // default is 'seconds'
|
||||
spec.dsec += delta;
|
||||
break;
|
||||
}
|
||||
// unreachable statement
|
||||
// throw new RrdException("Well-known time unit expected after " + delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try and read a "timeofday" specification. This method will be called
|
||||
* when we see a plain number at the start of a time, which means we could be
|
||||
* reading a time, or a day. If it turns out to be a date, then this method restores
|
||||
* the scanner state to what it was at entry, and returns without setting anything.
|
||||
* @throws RrdException
|
||||
*/
|
||||
private void timeOfDay() throws RrdException {
|
||||
int hour, minute = 0;
|
||||
/* save token status in case we must abort */
|
||||
scanner.saveState();
|
||||
/* first pick out the time of day - we assume a HH (COLON|DOT) MM time */
|
||||
if (token.value.length() > 2) {
|
||||
//Definitely not an hour specification; probably a date or something. Give up now
|
||||
return;
|
||||
}
|
||||
hour = Integer.parseInt(token.value);
|
||||
token = scanner.nextToken();
|
||||
if (token.id == TimeToken.SLASH) {
|
||||
/* guess we are looking at a date */
|
||||
token = scanner.restoreState();
|
||||
return;
|
||||
}
|
||||
if (token.id == TimeToken.COLON || token.id == TimeToken.DOT) {
|
||||
expectToken(TimeToken.NUMBER, "Parsing HH:MM or HH.MM syntax, expecting MM as number, got none");
|
||||
minute = Integer.parseInt(token.value);
|
||||
if (minute > 59) {
|
||||
throw new RrdException("Parsing HH:MM or HH.MM syntax, got MM = " +
|
||||
minute + " (>59!)");
|
||||
}
|
||||
token = scanner.nextToken();
|
||||
if(token.id == TimeToken.DOT) {
|
||||
//Oh look, another dot; must have actually been a date in DD.MM.YYYY format. Give up and return
|
||||
token = scanner.restoreState();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
/* check if an AM or PM specifier was given */
|
||||
if (token.id == TimeToken.AM || token.id == TimeToken.PM) {
|
||||
if (hour > 12) {
|
||||
throw new RrdException("There cannot be more than 12 AM or PM hours");
|
||||
}
|
||||
if (token.id == TimeToken.PM) {
|
||||
if (hour != 12) {
|
||||
/* 12:xx PM is 12:xx, not 24:xx */
|
||||
hour += 12;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hour == 12) {
|
||||
/* 12:xx AM is 00:xx, not 12:xx */
|
||||
hour = 0;
|
||||
}
|
||||
}
|
||||
token = scanner.nextToken();
|
||||
}
|
||||
else if (hour > 23) {
|
||||
/* guess it was not a time then, probably a date ... */
|
||||
token = scanner.restoreState();
|
||||
return;
|
||||
}
|
||||
|
||||
spec.hour = hour;
|
||||
spec.min = minute;
|
||||
spec.sec = 0;
|
||||
if (spec.hour == 24) {
|
||||
spec.hour = 0;
|
||||
spec.day++;
|
||||
}
|
||||
}
|
||||
|
||||
private void assignDate(long mday, long mon, long year) throws RrdException {
|
||||
if (year > 138) {
|
||||
if (year > 1970) {
|
||||
year -= 1900;
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Invalid year " + year +
|
||||
" (should be either 00-99 or >1900)");
|
||||
}
|
||||
}
|
||||
else if (year >= 0 && year < 38) {
|
||||
year += 100; /* Allow year 2000-2037 to be specified as */
|
||||
} /* 00-37 until the problem of 2038 year will */
|
||||
/* arise for unices with 32-bit time_t */
|
||||
if (year < 70) {
|
||||
throw new RrdException("Won't handle dates before epoch (01/01/1970), sorry");
|
||||
}
|
||||
spec.year = (int) year;
|
||||
spec.month = (int) mon;
|
||||
spec.day = (int) mday;
|
||||
}
|
||||
|
||||
private void day() throws RrdException {
|
||||
long mday = 0, wday, mon, year = spec.year;
|
||||
switch (token.id) {
|
||||
case TimeToken.YESTERDAY:
|
||||
spec.day--;
|
||||
token = scanner.nextToken();
|
||||
break;
|
||||
case TimeToken.TODAY: /* force ourselves to stay in today - no further processing */
|
||||
token = scanner.nextToken();
|
||||
break;
|
||||
case TimeToken.TOMORROW:
|
||||
spec.day++;
|
||||
token = scanner.nextToken();
|
||||
break;
|
||||
case TimeToken.JAN:
|
||||
case TimeToken.FEB:
|
||||
case TimeToken.MAR:
|
||||
case TimeToken.APR:
|
||||
case TimeToken.MAY:
|
||||
case TimeToken.JUN:
|
||||
case TimeToken.JUL:
|
||||
case TimeToken.AUG:
|
||||
case TimeToken.SEP:
|
||||
case TimeToken.OCT:
|
||||
case TimeToken.NOV:
|
||||
case TimeToken.DEC:
|
||||
/* do month mday [year] */
|
||||
mon = (token.id - TimeToken.JAN);
|
||||
expectToken(TimeToken.NUMBER, "the day of the month should follow month name");
|
||||
mday = Long.parseLong(token.value);
|
||||
token = scanner.nextToken();
|
||||
if (token.id == TimeToken.NUMBER) {
|
||||
year = Long.parseLong(token.value);
|
||||
token = scanner.nextToken();
|
||||
}
|
||||
else {
|
||||
year = spec.year;
|
||||
}
|
||||
assignDate(mday, mon, year);
|
||||
break;
|
||||
case TimeToken.SUN:
|
||||
case TimeToken.MON:
|
||||
case TimeToken.TUE:
|
||||
case TimeToken.WED:
|
||||
case TimeToken.THU:
|
||||
case TimeToken.FRI:
|
||||
case TimeToken.SAT:
|
||||
/* do a particular day of the week */
|
||||
wday = (token.id - TimeToken.SUN);
|
||||
spec.day += (wday - spec.wday);
|
||||
token = scanner.nextToken();
|
||||
break;
|
||||
case TimeToken.NUMBER:
|
||||
/* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY */
|
||||
// int tlen = token.value.length();
|
||||
mon = Long.parseLong(token.value);
|
||||
if (mon > 10L * 365L * 24L * 60L * 60L) {
|
||||
spec.localtime(mon);
|
||||
token = scanner.nextToken();
|
||||
break;
|
||||
}
|
||||
if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */
|
||||
year = mon / 10000;
|
||||
mday = mon % 100;
|
||||
mon = (mon / 100) % 100;
|
||||
token = scanner.nextToken();
|
||||
}
|
||||
else {
|
||||
token = scanner.nextToken();
|
||||
if (mon <= 31 && (token.id == TimeToken.SLASH || token.id == TimeToken.DOT)) {
|
||||
int sep = token.id;
|
||||
expectToken(TimeToken.NUMBER, "there should be " +
|
||||
(sep == TimeToken.DOT ? "month" : "day") +
|
||||
" number after " +
|
||||
(sep == TimeToken.DOT ? '.' : '/'));
|
||||
mday = Long.parseLong(token.value);
|
||||
token = scanner.nextToken();
|
||||
if (token.id == sep) {
|
||||
expectToken(TimeToken.NUMBER, "there should be year number after " +
|
||||
(sep == TimeToken.DOT ? '.' : '/'));
|
||||
year = Long.parseLong(token.value);
|
||||
token = scanner.nextToken();
|
||||
}
|
||||
/* flip months and days for European timing */
|
||||
if (sep == TimeToken.DOT) {
|
||||
long x = mday;
|
||||
mday = mon;
|
||||
mon = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
mon--;
|
||||
if (mon < 0 || mon > 11) {
|
||||
throw new RrdException("Did you really mean month " + (mon + 1));
|
||||
}
|
||||
if (mday < 1 || mday > 31) {
|
||||
throw new RrdException("I'm afraid that " + mday +
|
||||
" is not a valid day of the month");
|
||||
}
|
||||
assignDate(mday, mon, year);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the input string specified in the constructor.
|
||||
*
|
||||
* @return Object representing parsed date/time.
|
||||
* @throws RrdException Thrown if the date string cannot be parsed.
|
||||
*/
|
||||
public TimeSpec parse() throws RrdException {
|
||||
long now = Util.getTime();
|
||||
int hr = 0;
|
||||
/* this MUST be initialized to zero for midnight/noon/teatime */
|
||||
/* establish the default time reference */
|
||||
spec.localtime(now);
|
||||
token = scanner.nextToken();
|
||||
switch (token.id) {
|
||||
case TimeToken.PLUS:
|
||||
case TimeToken.MINUS:
|
||||
break; /* jump to OFFSET-SPEC part */
|
||||
case TimeToken.START:
|
||||
spec.type = TimeSpec.TYPE_START;
|
||||
/* FALLTHRU */
|
||||
case TimeToken.END:
|
||||
if (spec.type != TimeSpec.TYPE_START) {
|
||||
spec.type = TimeSpec.TYPE_END;
|
||||
}
|
||||
/* FALLTHRU */
|
||||
case TimeToken.EPOCH:
|
||||
/* FALLTHRU */
|
||||
case TimeToken.NOW:
|
||||
int time_reference = token.id;
|
||||
if (token.id != TimeToken.NOW) {
|
||||
spec.year = spec.month = spec.day = spec.hour = spec.min = spec.sec = 0;
|
||||
}
|
||||
token = scanner.nextToken();
|
||||
if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) {
|
||||
break;
|
||||
}
|
||||
if (time_reference == TimeToken.START || time_reference == TimeToken.END) {
|
||||
throw new RrdException("Words 'start' or 'end' MUST be followed by +|- offset");
|
||||
}
|
||||
else if (token.id != TimeToken.EOF) {
|
||||
throw new RrdException("If 'now' or 'epoch' is followed by a token it must be +|- offset");
|
||||
}
|
||||
break;
|
||||
/* Only absolute time specifications below */
|
||||
case TimeToken.NUMBER:
|
||||
timeOfDay();
|
||||
//Keep going; there might be a date after the time of day, which day() will pick up
|
||||
/* fix month parsing */
|
||||
case TimeToken.JAN:
|
||||
case TimeToken.FEB:
|
||||
case TimeToken.MAR:
|
||||
case TimeToken.APR:
|
||||
case TimeToken.MAY:
|
||||
case TimeToken.JUN:
|
||||
case TimeToken.JUL:
|
||||
case TimeToken.AUG:
|
||||
case TimeToken.SEP:
|
||||
case TimeToken.OCT:
|
||||
case TimeToken.NOV:
|
||||
case TimeToken.DEC:
|
||||
case TimeToken.TODAY:
|
||||
case TimeToken.YESTERDAY:
|
||||
case TimeToken.TOMORROW:
|
||||
day();
|
||||
if (token.id != TimeToken.NUMBER) {
|
||||
break;
|
||||
}
|
||||
//Allows (but does not require) the time to be specified after the day. This extends the rrdfetch specifiation
|
||||
timeOfDay();
|
||||
break;
|
||||
|
||||
/* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized
|
||||
* hr to zero up above, then fall into this case in such a
|
||||
* way so we add +12 +4 hours to it for teatime, +12 hours
|
||||
* to it for noon, and nothing at all for midnight, then
|
||||
* set our rettime to that hour before leaping into the
|
||||
* month scanner
|
||||
*/
|
||||
case TimeToken.TEATIME:
|
||||
hr += 4;
|
||||
/* FALLTHRU */
|
||||
case TimeToken.NOON:
|
||||
hr += 12;
|
||||
/* FALLTHRU */
|
||||
case TimeToken.MIDNIGHT:
|
||||
spec.hour = hr;
|
||||
spec.min = 0;
|
||||
spec.sec = 0;
|
||||
token = scanner.nextToken();
|
||||
day();
|
||||
break;
|
||||
default:
|
||||
throw new RrdException("Unparsable time: " + token.value);
|
||||
}
|
||||
|
||||
/*
|
||||
* the OFFSET-SPEC part
|
||||
*
|
||||
* (NOTE, the sc_tokid was prefetched for us by the previous code)
|
||||
*/
|
||||
if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) {
|
||||
scanner.setContext(false);
|
||||
while (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS ||
|
||||
token.id == TimeToken.NUMBER) {
|
||||
if (token.id == TimeToken.NUMBER) {
|
||||
plusMinus(PREVIOUS_OP);
|
||||
}
|
||||
else {
|
||||
plusMinus(token.id);
|
||||
}
|
||||
token = scanner.nextToken();
|
||||
/* We will get EOF eventually but that's OK, since
|
||||
token() will return us as many EOFs as needed */
|
||||
}
|
||||
}
|
||||
/* now we should be at EOF */
|
||||
if (token.id != TimeToken.EOF) {
|
||||
throw new RrdException("Unparsable trailing text: " + token.value);
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.core.timespec;
|
||||
|
||||
class TimeScanner {
|
||||
private String dateString;
|
||||
|
||||
private int pos, pos_save;
|
||||
private TimeToken token, token_save;
|
||||
|
||||
static final TimeToken[] WORDS = {
|
||||
new TimeToken("midnight", TimeToken.MIDNIGHT), /* 00:00:00 of today or tomorrow */
|
||||
new TimeToken("noon", TimeToken.NOON), /* 12:00:00 of today or tomorrow */
|
||||
new TimeToken("teatime", TimeToken.TEATIME), /* 16:00:00 of today or tomorrow */
|
||||
new TimeToken("am", TimeToken.AM), /* morning times for 0-12 clock */
|
||||
new TimeToken("pm", TimeToken.PM), /* evening times for 0-12 clock */
|
||||
new TimeToken("tomorrow", TimeToken.TOMORROW),
|
||||
new TimeToken("yesterday", TimeToken.YESTERDAY),
|
||||
new TimeToken("today", TimeToken.TODAY),
|
||||
new TimeToken("now", TimeToken.NOW),
|
||||
new TimeToken("n", TimeToken.NOW),
|
||||
new TimeToken("start", TimeToken.START),
|
||||
new TimeToken("s", TimeToken.START),
|
||||
new TimeToken("end", TimeToken.END),
|
||||
new TimeToken("e", TimeToken.END),
|
||||
new TimeToken("jan", TimeToken.JAN),
|
||||
new TimeToken("feb", TimeToken.FEB),
|
||||
new TimeToken("mar", TimeToken.MAR),
|
||||
new TimeToken("apr", TimeToken.APR),
|
||||
new TimeToken("may", TimeToken.MAY),
|
||||
new TimeToken("jun", TimeToken.JUN),
|
||||
new TimeToken("jul", TimeToken.JUL),
|
||||
new TimeToken("aug", TimeToken.AUG),
|
||||
new TimeToken("sep", TimeToken.SEP),
|
||||
new TimeToken("oct", TimeToken.OCT),
|
||||
new TimeToken("nov", TimeToken.NOV),
|
||||
new TimeToken("dec", TimeToken.DEC),
|
||||
new TimeToken("january", TimeToken.JAN),
|
||||
new TimeToken("february", TimeToken.FEB),
|
||||
new TimeToken("march", TimeToken.MAR),
|
||||
new TimeToken("april", TimeToken.APR),
|
||||
new TimeToken("may", TimeToken.MAY),
|
||||
new TimeToken("june", TimeToken.JUN),
|
||||
new TimeToken("july", TimeToken.JUL),
|
||||
new TimeToken("august", TimeToken.AUG),
|
||||
new TimeToken("september", TimeToken.SEP),
|
||||
new TimeToken("october", TimeToken.OCT),
|
||||
new TimeToken("november", TimeToken.NOV),
|
||||
new TimeToken("december", TimeToken.DEC),
|
||||
new TimeToken("sunday", TimeToken.SUN),
|
||||
new TimeToken("sun", TimeToken.SUN),
|
||||
new TimeToken("monday", TimeToken.MON),
|
||||
new TimeToken("mon", TimeToken.MON),
|
||||
new TimeToken("tuesday", TimeToken.TUE),
|
||||
new TimeToken("tue", TimeToken.TUE),
|
||||
new TimeToken("wednesday", TimeToken.WED),
|
||||
new TimeToken("wed", TimeToken.WED),
|
||||
new TimeToken("thursday", TimeToken.THU),
|
||||
new TimeToken("thu", TimeToken.THU),
|
||||
new TimeToken("friday", TimeToken.FRI),
|
||||
new TimeToken("fri", TimeToken.FRI),
|
||||
new TimeToken("saturday", TimeToken.SAT),
|
||||
new TimeToken("sat", TimeToken.SAT),
|
||||
new TimeToken("epoch", TimeToken.EPOCH),
|
||||
new TimeToken(null, 0) /*** SENTINEL ***/
|
||||
};
|
||||
|
||||
static TimeToken[] MULTIPLIERS = {
|
||||
new TimeToken("second", TimeToken.SECONDS), /* seconds multiplier */
|
||||
new TimeToken("seconds", TimeToken.SECONDS), /* (pluralized) */
|
||||
new TimeToken("sec", TimeToken.SECONDS), /* (generic) */
|
||||
new TimeToken("s", TimeToken.SECONDS), /* (short generic) */
|
||||
new TimeToken("minute", TimeToken.MINUTES), /* minutes multiplier */
|
||||
new TimeToken("minutes", TimeToken.MINUTES), /* (pluralized) */
|
||||
new TimeToken("min", TimeToken.MINUTES), /* (generic) */
|
||||
new TimeToken("m", TimeToken.MONTHS_MINUTES), /* (short generic) */
|
||||
new TimeToken("hour", TimeToken.HOURS), /* hours ... */
|
||||
new TimeToken("hours", TimeToken.HOURS), /* (pluralized) */
|
||||
new TimeToken("hr", TimeToken.HOURS), /* (generic) */
|
||||
new TimeToken("h", TimeToken.HOURS), /* (short generic) */
|
||||
new TimeToken("day", TimeToken.DAYS), /* days ... */
|
||||
new TimeToken("days", TimeToken.DAYS), /* (pluralized) */
|
||||
new TimeToken("d", TimeToken.DAYS), /* (short generic) */
|
||||
new TimeToken("week", TimeToken.WEEKS), /* week ... */
|
||||
new TimeToken("weeks", TimeToken.WEEKS), /* (pluralized) */
|
||||
new TimeToken("wk", TimeToken.WEEKS), /* (generic) */
|
||||
new TimeToken("w", TimeToken.WEEKS), /* (short generic) */
|
||||
new TimeToken("month", TimeToken.MONTHS), /* week ... */
|
||||
new TimeToken("months", TimeToken.MONTHS), /* (pluralized) */
|
||||
new TimeToken("mon", TimeToken.MONTHS), /* (generic) */
|
||||
new TimeToken("year", TimeToken.YEARS), /* year ... */
|
||||
new TimeToken("years", TimeToken.YEARS), /* (pluralized) */
|
||||
new TimeToken("yr", TimeToken.YEARS), /* (generic) */
|
||||
new TimeToken("y", TimeToken.YEARS), /* (short generic) */
|
||||
new TimeToken(null, 0) /*** SENTINEL ***/
|
||||
};
|
||||
|
||||
TimeToken[] specials = WORDS;
|
||||
|
||||
public TimeScanner(String dateString) {
|
||||
this.dateString = dateString;
|
||||
}
|
||||
|
||||
void setContext(boolean parsingWords) {
|
||||
specials = parsingWords ? WORDS : MULTIPLIERS;
|
||||
}
|
||||
|
||||
TimeToken nextToken() {
|
||||
StringBuffer buffer = new StringBuffer("");
|
||||
while (pos < dateString.length()) {
|
||||
char c = dateString.charAt(pos++);
|
||||
if (Character.isWhitespace(c) || c == '_' || c == ',') {
|
||||
continue;
|
||||
}
|
||||
buffer.append(c);
|
||||
if (Character.isDigit(c)) {
|
||||
// pick as many digits as possible
|
||||
while (pos < dateString.length()) {
|
||||
char next = dateString.charAt(pos);
|
||||
if (Character.isDigit(next)) {
|
||||
buffer.append(next);
|
||||
pos++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String value = buffer.toString();
|
||||
return token = new TimeToken(value, TimeToken.NUMBER);
|
||||
}
|
||||
if (Character.isLetter(c)) {
|
||||
// pick as many letters as possible
|
||||
while (pos < dateString.length()) {
|
||||
char next = dateString.charAt(pos);
|
||||
if (Character.isLetter(next)) {
|
||||
buffer.append(next);
|
||||
pos++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String value = buffer.toString();
|
||||
return token = new TimeToken(value, parseToken(value));
|
||||
}
|
||||
switch (c) {
|
||||
case ':':
|
||||
return token = new TimeToken(":", TimeToken.COLON);
|
||||
case '.':
|
||||
return token = new TimeToken(".", TimeToken.DOT);
|
||||
case '+':
|
||||
return token = new TimeToken("+", TimeToken.PLUS);
|
||||
case '-':
|
||||
return token = new TimeToken("-", TimeToken.MINUS);
|
||||
case '/':
|
||||
return token = new TimeToken("/", TimeToken.SLASH);
|
||||
default:
|
||||
pos--;
|
||||
return token = new TimeToken(null, TimeToken.EOF);
|
||||
}
|
||||
}
|
||||
return token = new TimeToken(null, TimeToken.EOF);
|
||||
}
|
||||
|
||||
TimeToken resolveMonthsMinutes(int newId) {
|
||||
assert token.id == TimeToken.MONTHS_MINUTES;
|
||||
assert newId == TimeToken.MONTHS || newId == TimeToken.MINUTES;
|
||||
return token = new TimeToken(token.value, newId);
|
||||
}
|
||||
|
||||
void saveState() {
|
||||
token_save = token;
|
||||
pos_save = pos;
|
||||
}
|
||||
|
||||
TimeToken restoreState() {
|
||||
pos = pos_save;
|
||||
return token = token_save;
|
||||
}
|
||||
|
||||
private int parseToken(String arg) {
|
||||
for (int i = 0; specials[i].value != null; i++) {
|
||||
if (specials[i].value.equalsIgnoreCase(arg)) {
|
||||
return specials[i].id;
|
||||
}
|
||||
}
|
||||
return TimeToken.ID;
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core.timespec;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
/**
|
||||
* Simple class to represent time obtained by parsing at-style date specification (described
|
||||
* in detail on the rrdfetch man page. See javadoc for {@link org.jrobin.core.timespec.TimeParser}
|
||||
* for more information.
|
||||
*/
|
||||
public class TimeSpec {
|
||||
static final int TYPE_ABSOLUTE = 0;
|
||||
static final int TYPE_START = 1;
|
||||
static final int TYPE_END = 2;
|
||||
|
||||
int type = TYPE_ABSOLUTE;
|
||||
int year, month, day, hour, min, sec;
|
||||
int wday;
|
||||
int dyear, dmonth, dday, dhour, dmin, dsec;
|
||||
|
||||
String dateString;
|
||||
|
||||
TimeSpec context;
|
||||
|
||||
TimeSpec(String dateString) {
|
||||
this.dateString = dateString;
|
||||
}
|
||||
|
||||
void localtime(long timestamp) {
|
||||
GregorianCalendar date = new GregorianCalendar();
|
||||
date.setTime(new Date(timestamp * 1000L));
|
||||
year = date.get(Calendar.YEAR) - 1900;
|
||||
month = date.get(Calendar.MONTH);
|
||||
day = date.get(Calendar.DAY_OF_MONTH);
|
||||
hour = date.get(Calendar.HOUR_OF_DAY);
|
||||
min = date.get(Calendar.MINUTE);
|
||||
sec = date.get(Calendar.SECOND);
|
||||
wday = date.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY;
|
||||
}
|
||||
|
||||
GregorianCalendar getTime() throws RrdException {
|
||||
GregorianCalendar gc;
|
||||
// absoulte time, this is easy
|
||||
if (type == TYPE_ABSOLUTE) {
|
||||
gc = new GregorianCalendar(year + 1900, month, day, hour, min, sec);
|
||||
}
|
||||
// relative time, we need a context to evaluate it
|
||||
else if (context != null && context.type == TYPE_ABSOLUTE) {
|
||||
gc = context.getTime();
|
||||
}
|
||||
// how would I guess what time it was?
|
||||
else {
|
||||
throw new RrdException("Relative times like '" +
|
||||
dateString + "' require proper absolute context to be evaluated");
|
||||
}
|
||||
gc.add(Calendar.YEAR, dyear);
|
||||
gc.add(Calendar.MONTH, dmonth);
|
||||
gc.add(Calendar.DAY_OF_MONTH, dday);
|
||||
gc.add(Calendar.HOUR_OF_DAY, dhour);
|
||||
gc.add(Calendar.MINUTE, dmin);
|
||||
gc.add(Calendar.SECOND, dsec);
|
||||
return gc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding timestamp (seconds since Epoch). Example:<p>
|
||||
* <pre>
|
||||
* TimeParser p = new TimeParser("now-1day");
|
||||
* TimeSpec ts = p.parse();
|
||||
* System.out.println("Timestamp was: " + ts.getTimestamp();
|
||||
* </pre>
|
||||
*
|
||||
* @return Timestamp (in seconds, no milliseconds)
|
||||
* @throws RrdException Thrown if this TimeSpec object does not represent absolute time.
|
||||
*/
|
||||
public long getTimestamp() throws RrdException {
|
||||
return Util.getTimestamp(getTime());
|
||||
}
|
||||
|
||||
String dump() {
|
||||
return (type == TYPE_ABSOLUTE ? "ABSTIME" : type == TYPE_START ? "START" : "END") +
|
||||
": " + year + "/" + month + "/" + day +
|
||||
"/" + hour + "/" + min + "/" + sec + " (" +
|
||||
dyear + "/" + dmonth + "/" + dday +
|
||||
"/" + dhour + "/" + dmin + "/" + dsec + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this static method to resolve relative time references and obtain the corresponding
|
||||
* Calendar objects. Example:<p>
|
||||
* <pre>
|
||||
* TimeParser pStart = new TimeParser("now-1month"); // starting time
|
||||
* TimeParser pEnd = new TimeParser("start+1week"); // ending time
|
||||
* TimeSpec specStart = pStart.parse();
|
||||
* TimeSpec specEnd = pEnd.parse();
|
||||
* GregorianCalendar[] gc = TimeSpec.getTimes(specStart, specEnd);
|
||||
* </pre>
|
||||
*
|
||||
* @param spec1 Starting time specification
|
||||
* @param spec2 Ending time specification
|
||||
* @return Two element array containing Calendar objects
|
||||
* @throws RrdException Thrown if relative time references cannot be resolved
|
||||
*/
|
||||
public static Calendar[] getTimes(TimeSpec spec1, TimeSpec spec2) throws RrdException {
|
||||
if (spec1.type == TYPE_START || spec2.type == TYPE_END) {
|
||||
throw new RrdException("Recursive time specifications not allowed");
|
||||
}
|
||||
spec1.context = spec2;
|
||||
spec2.context = spec1;
|
||||
return new Calendar[] {
|
||||
spec1.getTime(),
|
||||
spec2.getTime()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this static method to resolve relative time references and obtain the corresponding
|
||||
* timestamps (seconds since epoch). Example:<p>
|
||||
* <pre>
|
||||
* TimeParser pStart = new TimeParser("now-1month"); // starting time
|
||||
* TimeParser pEnd = new TimeParser("start+1week"); // ending time
|
||||
* TimeSpec specStart = pStart.parse();
|
||||
* TimeSpec specEnd = pEnd.parse();
|
||||
* long[] ts = TimeSpec.getTimestamps(specStart, specEnd);
|
||||
* </pre>
|
||||
*
|
||||
* @param spec1 Starting time specification
|
||||
* @param spec2 Ending time specification
|
||||
* @return array containing two timestamps (in seconds since epoch)
|
||||
* @throws RrdException Thrown if relative time references cannot be resolved
|
||||
*/
|
||||
public static long[] getTimestamps(TimeSpec spec1, TimeSpec spec2) throws RrdException {
|
||||
Calendar[] gcs = getTimes(spec1, spec2);
|
||||
return new long[] {
|
||||
Util.getTimestamp(gcs[0]), Util.getTimestamp(gcs[1])
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.core.timespec;
|
||||
|
||||
class TimeToken {
|
||||
public static final int MIDNIGHT = 1;
|
||||
public static final int NOON = 2;
|
||||
public static final int TEATIME = 3;
|
||||
public static final int PM = 4;
|
||||
public static final int AM = 5;
|
||||
public static final int YESTERDAY = 6;
|
||||
public static final int TODAY = 7;
|
||||
public static final int TOMORROW = 8;
|
||||
public static final int NOW = 9;
|
||||
public static final int START = 10;
|
||||
public static final int END = 11;
|
||||
public static final int SECONDS = 12;
|
||||
public static final int MINUTES = 13;
|
||||
public static final int HOURS = 14;
|
||||
public static final int DAYS = 15;
|
||||
public static final int WEEKS = 16;
|
||||
public static final int MONTHS = 17;
|
||||
public static final int YEARS = 18;
|
||||
public static final int MONTHS_MINUTES = 19;
|
||||
public static final int NUMBER = 20;
|
||||
public static final int PLUS = 21;
|
||||
public static final int MINUS = 22;
|
||||
public static final int DOT = 23;
|
||||
public static final int COLON = 24;
|
||||
public static final int SLASH = 25;
|
||||
public static final int ID = 26;
|
||||
public static final int JUNK = 27;
|
||||
public static final int JAN = 28;
|
||||
public static final int FEB = 29;
|
||||
public static final int MAR = 30;
|
||||
public static final int APR = 31;
|
||||
public static final int MAY = 32;
|
||||
public static final int JUN = 33;
|
||||
public static final int JUL = 34;
|
||||
public static final int AUG = 35;
|
||||
public static final int SEP = 36;
|
||||
public static final int OCT = 37;
|
||||
public static final int NOV = 38;
|
||||
public static final int DEC = 39;
|
||||
public static final int SUN = 40;
|
||||
public static final int MON = 41;
|
||||
public static final int TUE = 42;
|
||||
public static final int WED = 43;
|
||||
public static final int THU = 44;
|
||||
public static final int FRI = 45;
|
||||
public static final int SAT = 46;
|
||||
public static final int EPOCH = 46;
|
||||
public static final int EOF = -1;
|
||||
|
||||
final String value; /* token name */
|
||||
final int id; /* token id */
|
||||
|
||||
public TimeToken(String value, int id) {
|
||||
this.value = value;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value + " [" + id + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.ConsolFuns;
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
/**
|
||||
* Simple class which holds aggregated values (MIN, MAX, FIRST, LAST, AVERAGE and TOTAL). You
|
||||
* don't need to create objects of this class directly. Objects of this class are returned from
|
||||
* <code>getAggregates()</code> method in
|
||||
* {@link org.jrobin.core.FetchData#getAggregates(String) FetchData} and
|
||||
* {@link DataProcessor#getAggregates(String)} DataProcessor} classes.
|
||||
*/
|
||||
public class Aggregates implements ConsolFuns {
|
||||
double min = Double.NaN, max = Double.NaN;
|
||||
double first = Double.NaN, last = Double.NaN;
|
||||
double average = Double.NaN, total = Double.NaN;
|
||||
double stdev = Double.NaN, lslslope = Double.NaN;
|
||||
double lslint = Double.NaN, lslcorrel = Double.NaN;
|
||||
|
||||
Aggregates() {
|
||||
// NOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimal value
|
||||
*
|
||||
* @return Minimal value
|
||||
*/
|
||||
public double getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum value
|
||||
*
|
||||
* @return Maximum value
|
||||
*/
|
||||
public double getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first falue
|
||||
*
|
||||
* @return First value
|
||||
*/
|
||||
public double getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last value
|
||||
*
|
||||
* @return Last value
|
||||
*/
|
||||
public double getLast() {
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns average
|
||||
*
|
||||
* @return Average value
|
||||
*/
|
||||
public double getAverage() {
|
||||
return average;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns total value
|
||||
*
|
||||
* @return Total value
|
||||
*/
|
||||
public double getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns stdev value
|
||||
*
|
||||
* @return Stdev value
|
||||
*/
|
||||
public double getStdev() {
|
||||
return stdev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Least Squares Line Slope value
|
||||
*
|
||||
* @return lslslope value
|
||||
*/
|
||||
public double getLSLSlope() {
|
||||
return stdev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Least Squares Line y-intercept value
|
||||
*
|
||||
* @return lslint value
|
||||
*/
|
||||
public double getLSLInt() {
|
||||
return lslint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Least Squares Line Correlation Coefficient
|
||||
*
|
||||
* @return lslcorrel value
|
||||
*/
|
||||
public double getLSLCorrel() {
|
||||
return lslcorrel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns single aggregated value for the give consolidation function
|
||||
*
|
||||
* @param consolFun Consolidation function: MIN, MAX, FIRST, LAST, AVERAGE, TOTAL. These constants
|
||||
* are conveniently defined in the {@link org.jrobin.core.ConsolFuns ConsolFuns} interface.
|
||||
* @return Aggregated value
|
||||
* @throws RrdException Thrown if unsupported consolidation function is supplied
|
||||
*/
|
||||
public double getAggregate(String consolFun) throws RrdException {
|
||||
if (consolFun.equals(CF_AVERAGE)) {
|
||||
return average;
|
||||
}
|
||||
else if (consolFun.equals(CF_FIRST)) {
|
||||
return first;
|
||||
}
|
||||
else if (consolFun.equals(CF_LAST)) {
|
||||
return last;
|
||||
}
|
||||
else if (consolFun.equals(CF_MAX)) {
|
||||
return max;
|
||||
}
|
||||
else if (consolFun.equals(CF_MIN)) {
|
||||
return min;
|
||||
}
|
||||
else if (consolFun.equals(CF_TOTAL)) {
|
||||
return total;
|
||||
}
|
||||
else if (consolFun.equals("STDEV")) {
|
||||
return stdev;
|
||||
}
|
||||
else if (consolFun.equals("LSLSLOPE")) {
|
||||
return lslslope;
|
||||
}
|
||||
else if (consolFun.equals("LSLINT")) {
|
||||
return lslint;
|
||||
}
|
||||
else if (consolFun.equals("LSLCORREL")) {
|
||||
return lslcorrel;
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Unknown consolidation function: " + consolFun);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns String representing all aggregated values. Just for debugging purposes.
|
||||
*
|
||||
* @return String containing all aggregated values
|
||||
*/
|
||||
public String dump() {
|
||||
return "MIN=" + Util.formatDouble(min) + ", MAX=" + Util.formatDouble(max) + "\n" +
|
||||
"FIRST=" + Util.formatDouble(first) + ", LAST=" + Util.formatDouble(last) + "\n" +
|
||||
"AVERAGE=" + Util.formatDouble(average) + ", TOTAL=" + Util.formatDouble(total);
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.ConsolFuns;
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
class Aggregator implements ConsolFuns {
|
||||
private long timestamps[], step;
|
||||
private double[] values;
|
||||
|
||||
Aggregator(long[] timestamps, double[] values) {
|
||||
assert timestamps.length == values.length: "Incompatible timestamps/values arrays (unequal lengths)";
|
||||
assert timestamps.length >= 2: "At least two timestamps must be supplied";
|
||||
this.timestamps = timestamps;
|
||||
this.values = values;
|
||||
this.step = timestamps[1] - timestamps[0];
|
||||
}
|
||||
|
||||
Aggregates getAggregates(long tStart, long tEnd) {
|
||||
Aggregates agg = new Aggregates();
|
||||
int cnt = 0;
|
||||
int lslstep = 0;
|
||||
boolean firstFound = false;
|
||||
double SUMx, SUMy, SUMxy, SUMxx, SUMyy;
|
||||
SUMx = 0.0;
|
||||
SUMy = 0.0;
|
||||
SUMxy = 0.0;
|
||||
SUMxx = 0.0;
|
||||
SUMyy = 0.0;
|
||||
|
||||
for (int i = 0; i < timestamps.length; i++) {
|
||||
long left = Math.max(timestamps[i] - step, tStart);
|
||||
long right = Math.min(timestamps[i], tEnd);
|
||||
long delta = right - left;
|
||||
|
||||
// delta is only >= 0 when the timestamp for a given buck is within the range of tStart and tEnd
|
||||
if (delta >= 0) {
|
||||
double value = values[i];
|
||||
agg.min = Util.min(agg.min, value);
|
||||
agg.max = Util.max(agg.max, value);
|
||||
if (!firstFound) {
|
||||
agg.first = value;
|
||||
firstFound = true;
|
||||
agg.last = value;
|
||||
} else if (delta >= step) { // an entire bucket is included in this range
|
||||
agg.last = value;
|
||||
|
||||
/*
|
||||
* Algorithmically, we're only updating last if it's either the first
|
||||
* bucket encountered, or it's a "full" bucket.
|
||||
|
||||
if ( !isInRange(tEnd, left, right) ||
|
||||
(isInRange(tEnd, left, right) && !Double.isNaN(value))
|
||||
) {
|
||||
agg.last = value;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
if (!Double.isNaN(value)) {
|
||||
cnt++;
|
||||
SUMx += lslstep;
|
||||
SUMxx += lslstep * lslstep;
|
||||
SUMy = Util.sum(SUMy, value);
|
||||
SUMxy = Util.sum(SUMxy, lslstep * value);
|
||||
SUMyy = Util.sum(SUMyy, value * value);
|
||||
}
|
||||
lslstep ++;
|
||||
}
|
||||
}
|
||||
agg.average = cnt > 0 ? (SUMy / cnt) : Double.NaN;
|
||||
|
||||
// Work on STDEV
|
||||
if (cnt > 0) {
|
||||
double stdevSum = 0.0;
|
||||
for (int i = 0; i < timestamps.length; i++) {
|
||||
long left = Math.max(timestamps[i] - step, tStart);
|
||||
long right = Math.min(timestamps[i], tEnd);
|
||||
long delta = right - left;
|
||||
|
||||
// delta is only >= 0 when the timestamp for a given buck is within the range of tStart and tEnd
|
||||
if (delta >= 0) {
|
||||
double value = values[i];
|
||||
if (!Double.isNaN(value)) {
|
||||
stdevSum = Util.sum(stdevSum, Math.pow((value - agg.average), 2.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
agg.stdev = Math.pow(stdevSum / cnt, 0.5);
|
||||
|
||||
/* Bestfit line by linear least squares method */
|
||||
agg.lslslope = (SUMx * SUMy - cnt * SUMxy) / (SUMx * SUMx - cnt * SUMxx);
|
||||
agg.lslint = (SUMy - agg.lslslope * SUMx) / cnt;
|
||||
agg.lslcorrel =
|
||||
(SUMxy - (SUMx * SUMy) / cnt) /
|
||||
Math.sqrt((SUMxx - (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt));
|
||||
}
|
||||
agg.total = SUMy * step;
|
||||
|
||||
return agg;
|
||||
}
|
||||
|
||||
double getPercentile(long tStart, long tEnd, double percentile) {
|
||||
return getPercentile(tStart, tEnd, percentile, false);
|
||||
}
|
||||
|
||||
double getPercentile(long tStart, long tEnd, double percentile, boolean includenan) {
|
||||
List<Double> valueList = new ArrayList<Double>();
|
||||
// create a list of included datasource values (different from NaN)
|
||||
for (int i = 0; i < timestamps.length; i++) {
|
||||
long left = Math.max(timestamps[i] - step, tStart);
|
||||
long right = Math.min(timestamps[i], tEnd);
|
||||
if (right > left && (!Double.isNaN(values[i]) || includenan)) {
|
||||
valueList.add(values[i]);
|
||||
}
|
||||
}
|
||||
// create an array to work with
|
||||
int count = valueList.size();
|
||||
if (count > 1) {
|
||||
double[] valuesCopy = new double[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
valuesCopy[i] = valueList.get(i);
|
||||
}
|
||||
// sort array
|
||||
Arrays.sort(valuesCopy);
|
||||
// skip top (100% - percentile) values
|
||||
double topPercentile = (100.0 - percentile) / 100.0;
|
||||
count -= (int) Math.ceil(count * topPercentile);
|
||||
// if we have anything left...
|
||||
if (count > 0) {
|
||||
return valuesCopy[count - 1];
|
||||
}
|
||||
}
|
||||
// not enough data available
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
class CDef extends Source {
|
||||
private String rpnExpression;
|
||||
|
||||
CDef(String name, String rpnExpression) {
|
||||
super(name);
|
||||
this.rpnExpression = rpnExpression;
|
||||
}
|
||||
|
||||
String getRpnExpression() {
|
||||
return rpnExpression;
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Class used to interpolate datasource values from the collection of (timestamp, values)
|
||||
* points using natural cubic spline interpolation.
|
||||
* <p>
|
||||
* <b>WARNING</b>: So far, this class cannot handle NaN datasource values
|
||||
* (an exception will be thrown by the constructor). Future releases might change this.
|
||||
*/
|
||||
public class CubicSplineInterpolator extends Plottable {
|
||||
private double[] x;
|
||||
private double[] y;
|
||||
|
||||
// second derivates come here
|
||||
private double[] y2;
|
||||
|
||||
// internal spline variables
|
||||
private int n, klo, khi;
|
||||
|
||||
/**
|
||||
* Creates cubic spline interpolator from arrays of timestamps and corresponding
|
||||
* datasource values.
|
||||
*
|
||||
* @param timestamps timestamps in seconds
|
||||
* @param values corresponding datasource values
|
||||
* @throws RrdException Thrown if supplied arrays do not contain at least 3 values, or if
|
||||
* timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
|
||||
*/
|
||||
public CubicSplineInterpolator(long[] timestamps, double[] values) throws RrdException {
|
||||
this.x = new double[timestamps.length];
|
||||
for (int i = 0; i < timestamps.length; i++) {
|
||||
this.x[i] = timestamps[i];
|
||||
}
|
||||
this.y = values;
|
||||
validate();
|
||||
spline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates cubic spline interpolator from arrays of Date objects and corresponding
|
||||
* datasource values.
|
||||
*
|
||||
* @param dates Array of Date objects
|
||||
* @param values corresponding datasource values
|
||||
* @throws RrdException Thrown if supplied arrays do not contain at least 3 values, or if
|
||||
* timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
|
||||
*/
|
||||
public CubicSplineInterpolator(Date[] dates, double[] values) throws RrdException {
|
||||
this.x = new double[dates.length];
|
||||
for (int i = 0; i < dates.length; i++) {
|
||||
this.x[i] = Util.getTimestamp(dates[i]);
|
||||
}
|
||||
this.y = values;
|
||||
validate();
|
||||
spline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates cubic spline interpolator from arrays of GregorianCalendar objects and corresponding
|
||||
* datasource values.
|
||||
*
|
||||
* @param dates Array of GregorianCalendar objects
|
||||
* @param values corresponding datasource values
|
||||
* @throws RrdException Thrown if supplied arrays do not contain at least 3 values, or if
|
||||
* timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
|
||||
*/
|
||||
public CubicSplineInterpolator(Calendar[] dates, double[] values) throws RrdException {
|
||||
this.x = new double[dates.length];
|
||||
for (int i = 0; i < dates.length; i++) {
|
||||
this.x[i] = Util.getTimestamp(dates[i]);
|
||||
}
|
||||
this.y = values;
|
||||
validate();
|
||||
spline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates cubic spline interpolator for an array of 2D-points.
|
||||
*
|
||||
* @param x x-axis point coordinates
|
||||
* @param y y-axis point coordinates
|
||||
* @throws RrdException Thrown if supplied arrays do not contain at least 3 values, or if
|
||||
* timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
|
||||
*/
|
||||
public CubicSplineInterpolator(double[] x, double[] y) throws RrdException {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
validate();
|
||||
spline();
|
||||
}
|
||||
|
||||
private void validate() throws RrdException {
|
||||
boolean ok = true;
|
||||
if (x.length != y.length || x.length < 3) {
|
||||
ok = false;
|
||||
}
|
||||
for (int i = 0; i < x.length - 1 && ok; i++) {
|
||||
if (x[i] >= x[i + 1] || Double.isNaN(y[i])) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
throw new RrdException("Invalid plottable data supplied");
|
||||
}
|
||||
}
|
||||
|
||||
private void spline() {
|
||||
n = x.length;
|
||||
y2 = new double[n];
|
||||
double[] u = new double[n - 1];
|
||||
y2[0] = y2[n - 1] = 0.0;
|
||||
u[0] = 0.0; // natural spline
|
||||
for (int i = 1; i <= n - 2; i++) {
|
||||
double sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
|
||||
double p = sig * y2[i - 1] + 2.0;
|
||||
y2[i] = (sig - 1.0) / p;
|
||||
u[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
|
||||
u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
|
||||
}
|
||||
for (int k = n - 2; k >= 0; k--) {
|
||||
y2[k] = y2[k] * y2[k + 1] + u[k];
|
||||
}
|
||||
// prepare everything for getValue()
|
||||
klo = 0;
|
||||
khi = n - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates spline-interpolated y-value for the corresponding x-value. Call
|
||||
* this if you need spline-interpolated values in your code.
|
||||
*
|
||||
* @param xval x-value
|
||||
* @return inteprolated y-value
|
||||
*/
|
||||
public double getValue(double xval) {
|
||||
if (xval < x[0] || xval > x[n - 1]) {
|
||||
return Double.NaN;
|
||||
}
|
||||
if (xval < x[klo] || xval > x[khi]) {
|
||||
// out of bounds
|
||||
klo = 0;
|
||||
khi = n - 1;
|
||||
}
|
||||
while (khi - klo > 1) {
|
||||
// find bounding interval using bisection method
|
||||
int k = (khi + klo) >>> 1;
|
||||
if (x[k] > xval) {
|
||||
khi = k;
|
||||
}
|
||||
else {
|
||||
klo = k;
|
||||
}
|
||||
}
|
||||
double h = x[khi] - x[klo];
|
||||
double a = (x[khi] - xval) / h;
|
||||
double b = (xval - x[klo]) / h;
|
||||
return a * y[klo] + b * y[khi] +
|
||||
((a * a * a - a) * y2[klo] + (b * b * b - b) * y2[khi]) * (h * h) / 6.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method overriden from the base class. This method will be called by the framework. Call
|
||||
* this method only if you need spline-interpolated values in your code.
|
||||
*
|
||||
* @param timestamp timestamp in seconds
|
||||
* @return inteprolated datasource value
|
||||
*/
|
||||
public double getValue(long timestamp) {
|
||||
return getValue((double) timestamp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,936 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Class which should be used for all calculations based on the data fetched from RRD files. This class
|
||||
* supports ordinary DEF datasources (defined in RRD files), CDEF datasources (RPN expressions evaluation),
|
||||
* SDEF (static datasources - extension of JRobin) and PDEF (plottables, see
|
||||
* {@link Plottable Plottable} for more information.
|
||||
* <p>
|
||||
* Typical class usage:
|
||||
* <p>
|
||||
* <pre>
|
||||
* final long t1 = ...
|
||||
* final long t2 = ...
|
||||
* DataProcessor dp = new DataProcessor(t1, t2);
|
||||
* // DEF datasource
|
||||
* dp.addDatasource("x", "demo.rrd", "some_source", "AVERAGE");
|
||||
* // DEF datasource
|
||||
* dp.addDatasource("y", "demo.rrd", "some_other_source", "AVERAGE");
|
||||
* // CDEF datasource, z = (x + y) / 2
|
||||
* dp.addDatasource("z", "x,y,+,2,/");
|
||||
* // ACTION!
|
||||
* dp.processData();
|
||||
* // Dump calculated values
|
||||
* System.out.println(dp.dump());
|
||||
* </pre>
|
||||
*/
|
||||
public class DataProcessor implements ConsolFuns {
|
||||
/**
|
||||
* Constant representing the default number of pixels on a JRobin graph (will be used if
|
||||
* no other value is specified with {@link #setStep(long) setStep()} method.
|
||||
*/
|
||||
public static final int DEFAULT_PIXEL_COUNT = 600;
|
||||
private static final double DEFAULT_PERCENTILE = 95.0; // %
|
||||
|
||||
private int pixelCount = DEFAULT_PIXEL_COUNT;
|
||||
|
||||
/**
|
||||
* Constant that defines the default {@link RrdDbPool} usage policy. Defaults to <code>false</code>
|
||||
* (i.e. the pool will not be used to fetch data from RRD files)
|
||||
*/
|
||||
public static final boolean DEFAULT_POOL_USAGE_POLICY = false;
|
||||
private boolean poolUsed = DEFAULT_POOL_USAGE_POLICY;
|
||||
|
||||
private final long tStart;
|
||||
private long tEnd, timestamps[];
|
||||
private long lastRrdArchiveUpdateTime = 0;
|
||||
// this will be adjusted later
|
||||
private long step = 0;
|
||||
// resolution to be used for RRD fetch operation
|
||||
private long fetchRequestResolution = 1;
|
||||
|
||||
// the order is important, ordinary HashMap is unordered
|
||||
private Map<String, Source> sources = new LinkedHashMap<String, Source>();
|
||||
|
||||
private Def[] defSources;
|
||||
|
||||
/**
|
||||
* Creates new DataProcessor object for the given time span. Ending timestamp may be set to zero.
|
||||
* In that case, the class will try to find the optimal ending timestamp based on the last update time of
|
||||
* RRD files processed with the {@link #processData()} method.
|
||||
*
|
||||
* @param t1 Starting timestamp in seconds without milliseconds
|
||||
* @param t2 Ending timestamp in seconds without milliseconds
|
||||
* @throws RrdException Thrown if invalid timestamps are supplied
|
||||
*/
|
||||
public DataProcessor(long t1, long t2) throws RrdException {
|
||||
if ((t1 < t2 && t1 > 0 && t2 > 0) || (t1 > 0 && t2 == 0)) {
|
||||
this.tStart = t1;
|
||||
this.tEnd = t2;
|
||||
}
|
||||
else {
|
||||
throw new RrdException("Invalid timestamps specified: " + t1 + ", " + t2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new DataProcessor object for the given time span. Ending date may be set to null.
|
||||
* In that case, the class will try to find optimal ending date based on the last update time of
|
||||
* RRD files processed with the {@link #processData()} method.
|
||||
*
|
||||
* @param d1 Starting date
|
||||
* @param d2 Ending date
|
||||
* @throws RrdException Thrown if invalid timestamps are supplied
|
||||
*/
|
||||
public DataProcessor(Date d1, Date d2) throws RrdException {
|
||||
this(Util.getTimestamp(d1), d2 != null ? Util.getTimestamp(d2) : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new DataProcessor object for the given time span. Ending date may be set to null.
|
||||
* In that case, the class will try to find optimal ending date based on the last update time of
|
||||
* RRD files processed with the {@link #processData()} method.
|
||||
*
|
||||
* @param gc1 Starting Calendar date
|
||||
* @param gc2 Ending Calendar date
|
||||
* @throws RrdException Thrown if invalid timestamps are supplied
|
||||
*/
|
||||
public DataProcessor(Calendar gc1, Calendar gc2) throws RrdException {
|
||||
this(Util.getTimestamp(gc1), gc2 != null ? Util.getTimestamp(gc2) : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean value representing {@link org.jrobin.core.RrdDbPool RrdDbPool} usage policy.
|
||||
*
|
||||
* @return true, if the pool will be used internally to fetch data from RRD files, false otherwise.
|
||||
*/
|
||||
public boolean isPoolUsed() {
|
||||
return poolUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link org.jrobin.core.RrdDbPool RrdDbPool} usage policy.
|
||||
*
|
||||
* @param poolUsed true, if the pool should be used to fetch data from RRD files, false otherwise.
|
||||
*/
|
||||
public void setPoolUsed(boolean poolUsed) {
|
||||
this.poolUsed = poolUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of pixels (target graph width). This number is used only to calculate pixel coordinates
|
||||
* for JRobin graphs (methods {@link #getValuesPerPixel(String)} and {@link #getTimestampsPerPixel()}),
|
||||
* but has influence neither on datasource values calculated with the
|
||||
* {@link #processData()} method nor on aggregated values returned from {@link #getAggregates(String)}
|
||||
* and similar methods. In other words, aggregated values will not change once you decide to change
|
||||
* the dimension of your graph.
|
||||
* <p>
|
||||
* The default number of pixels is defined by constant {@link #DEFAULT_PIXEL_COUNT}
|
||||
* and can be changed with a {@link #setPixelCount(int)} method.
|
||||
*
|
||||
* @param pixelCount The number of pixels. If you process RRD data in order to display it on the graph,
|
||||
* this should be the width of your graph.
|
||||
*/
|
||||
public void setPixelCount(int pixelCount) {
|
||||
this.pixelCount = pixelCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of pixels (target graph width). See {@link #setPixelCount(int)} for more information.
|
||||
*
|
||||
* @return Target graph width
|
||||
*/
|
||||
public int getPixelCount() {
|
||||
return pixelCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Roughly corresponds to the --step option in RRDTool's graph/xport commands. Here is an explanation borrowed
|
||||
* from RRDTool:
|
||||
* <p>
|
||||
* <i>"By default rrdgraph calculates the width of one pixel in the time
|
||||
* domain and tries to get data at that resolution from the RRD. With
|
||||
* this switch you can override this behavior. If you want rrdgraph to
|
||||
* get data at 1 hour resolution from the RRD, then you can set the
|
||||
* step to 3600 seconds. Note, that a step smaller than 1 pixel will
|
||||
* be silently ignored."</i>
|
||||
* <p>
|
||||
* I think this option is not that useful, but it's here just for compatibility.
|
||||
* <p>
|
||||
* @param step Time step at which data should be fetched from RRD files. If this method is not used,
|
||||
* the step will be equal to the smallest RRD step of all processed RRD files. If no RRD file is processed,
|
||||
* the step will be roughly equal to the with of one graph pixel (in seconds).
|
||||
*/
|
||||
public void setStep(long step) {
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time step used for data processing. Initially, this method returns zero.
|
||||
* Once {@link #processData()} is finished, the method will return the real value used for
|
||||
* all internal computations. Roughly corresponds to the --step option in RRDTool's graph/xport commands.
|
||||
*
|
||||
* @return Step used for data processing.
|
||||
*/
|
||||
public long getStep() {
|
||||
return step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns desired RRD archive step (reslution) in seconds to be used while fetching data
|
||||
* from RRD files. In other words, this value will used as the last parameter of
|
||||
* {@link RrdDb#createFetchRequest(String, long, long, long) RrdDb.createFetchRequest()} method
|
||||
* when this method is called internally by this DataProcessor.
|
||||
*
|
||||
* @return Desired archive step (fetch resolution) in seconds.
|
||||
*/
|
||||
public long getFetchRequestResolution() {
|
||||
return fetchRequestResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets desired RRD archive step in seconds to be used internally while fetching data
|
||||
* from RRD files. In other words, this value will used as the last parameter of
|
||||
* {@link RrdDb#createFetchRequest(String, long, long, long) RrdDb.createFetchRequest()} method
|
||||
* when this method is called internally by this DataProcessor. If this method is never called, fetch
|
||||
* request resolution defaults to 1 (smallest possible archive step will be chosen automatically).
|
||||
*
|
||||
* @param fetchRequestResolution Desired archive step (fetch resoltuion) in seconds.
|
||||
*/
|
||||
public void setFetchRequestResolution(long fetchRequestResolution) {
|
||||
this.fetchRequestResolution = fetchRequestResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ending timestamp. Basically, this value is equal to the ending timestamp
|
||||
* specified in the constructor. However, if the ending timestamps was zero, it
|
||||
* will be replaced with the real timestamp when the {@link #processData()} method returns. The real
|
||||
* value will be calculated from the last update times of processed RRD files.
|
||||
*
|
||||
* @return Ending timestamp in seconds
|
||||
*/
|
||||
public long getEndingTimestamp() {
|
||||
return tEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns consolidated timestamps created with the {@link #processData()} method.
|
||||
*
|
||||
* @return array of timestamps in seconds
|
||||
* @throws RrdException thrown if timestamps are not calculated yet
|
||||
*/
|
||||
public long[] getTimestamps() throws RrdException {
|
||||
if (timestamps == null) {
|
||||
throw new RrdException("Timestamps not calculated yet");
|
||||
}
|
||||
else {
|
||||
return timestamps;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns calculated values for a single datasource. Corresponding timestamps can be obtained from
|
||||
* the {@link #getTimestamps()} method.
|
||||
*
|
||||
* @param sourceName Datasource name
|
||||
* @return an array of datasource values
|
||||
* @throws RrdException Thrown if invalid datasource name is specified,
|
||||
* or if datasource values are not yet calculated (method {@link #processData()}
|
||||
* was not called)
|
||||
*/
|
||||
public double[] getValues(String sourceName) throws RrdException {
|
||||
Source source = getSource(sourceName);
|
||||
double[] values = source.getValues();
|
||||
if (values == null) {
|
||||
throw new RrdException("Values not available for source [" + sourceName + "]");
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns single aggregated value for a single datasource.
|
||||
*
|
||||
* @param sourceName Datasource name
|
||||
* @param consolFun Consolidation function to be applied to fetched datasource values.
|
||||
* Valid consolidation functions are MIN, MAX, LAST, FIRST, AVERAGE and TOTAL
|
||||
* (these string constants are conveniently defined in the {@link ConsolFuns} class)
|
||||
* @return MIN, MAX, LAST, FIRST, AVERAGE or TOTAL value calculated from the data
|
||||
* for the given datasource name
|
||||
* @throws RrdException Thrown if invalid datasource name is specified,
|
||||
* or if datasource values are not yet calculated (method {@link #processData()}
|
||||
* was not called)
|
||||
*/
|
||||
public double getAggregate(String sourceName, String consolFun) throws RrdException {
|
||||
Source source = getSource(sourceName);
|
||||
return source.getAggregates(tStart, tEnd).getAggregate(consolFun);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all (MIN, MAX, LAST, FIRST, AVERAGE and TOTAL) aggregated values for a single datasource.
|
||||
*
|
||||
* @param sourceName Datasource name
|
||||
* @return Object containing all aggregated values
|
||||
* @throws RrdException Thrown if invalid datasource name is specified,
|
||||
* or if datasource values are not yet calculated (method {@link #processData()}
|
||||
* was not called)
|
||||
*/
|
||||
public Aggregates getAggregates(String sourceName) throws RrdException {
|
||||
Source source = getSource(sourceName);
|
||||
return source.getAggregates(tStart, tEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is just an alias for {@link #getPercentile(String)} method.
|
||||
* <p>
|
||||
* Used by ISPs which charge for bandwidth utilization on a "95th percentile" basis.
|
||||
* <p>
|
||||
* The 95th percentile is the highest source value left when the top 5% of a numerically sorted set
|
||||
* of source data is discarded. It is used as a measure of the peak value used when one discounts
|
||||
* a fair amount for transitory spikes. This makes it markedly different from the average.
|
||||
* <p>
|
||||
* Read more about this topic at
|
||||
* <a href="http://www.red.net/support/resourcecentre/leasedline/percentile.php">Rednet</a> or
|
||||
* <a href="http://www.bytemark.co.uk/support/tech/95thpercentile.html">Bytemark</a>.
|
||||
*
|
||||
* @param sourceName Datasource name
|
||||
* @return 95th percentile of fetched source values
|
||||
* @throws RrdException Thrown if invalid source name is supplied
|
||||
*/
|
||||
public double get95Percentile(String sourceName) throws RrdException {
|
||||
return getPercentile(sourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by ISPs which charge for bandwidth utilization on a "95th percentile" basis.
|
||||
* <p>
|
||||
* The 95th percentile is the highest source value left when the top 5% of a numerically sorted set
|
||||
* of source data is discarded. It is used as a measure of the peak value used when one discounts
|
||||
* a fair amount for transitory spikes. This makes it markedly different from the average.
|
||||
* <p>
|
||||
* Read more about this topic at
|
||||
* <a href="http://www.red.net/support/resourcecentre/leasedline/percentile.php">Rednet</a> or
|
||||
* <a href="http://www.bytemark.co.uk/support/tech/95thpercentile.html">Bytemark</a>.
|
||||
*
|
||||
* @param sourceName Datasource name
|
||||
* @return 95th percentile of fetched source values
|
||||
* @throws RrdException Thrown if invalid source name is supplied
|
||||
*/
|
||||
public double getPercentile(String sourceName) throws RrdException {
|
||||
return getPercentile(sourceName, DEFAULT_PERCENTILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as {@link #getPercentile(String)} but with a possibility to define custom percentile boundary
|
||||
* (different from 95).
|
||||
*
|
||||
* @param sourceName Datasource name.
|
||||
* @param percentile Boundary percentile. Value of 95 (%) is suitable in most cases, but you are free
|
||||
* to provide your own percentile boundary between zero and 100.
|
||||
* @return Requested percentile of fetched source values
|
||||
* @throws RrdException Thrown if invalid sourcename is supplied, or if the percentile value makes no sense.
|
||||
*/
|
||||
public double getPercentile(String sourceName, double percentile) throws RrdException {
|
||||
if (percentile <= 0.0 || percentile > 100.0) {
|
||||
throw new RrdException("Invalid percentile [" + percentile + "], should be between 0 and 100");
|
||||
}
|
||||
Source source = getSource(sourceName);
|
||||
return source.getPercentile(tStart, tEnd, percentile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of datasource names defined in this DataProcessor.
|
||||
*
|
||||
* @return array of datasource names
|
||||
*/
|
||||
public String[] getSourceNames() {
|
||||
return sources.keySet().toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all datasource values for all datasources. Each row in this two-dimensional
|
||||
* array represents an array of calculated values for a single datasource. The order of rows is the same
|
||||
* as the order in which datasources were added to this DataProcessor object.
|
||||
*
|
||||
* @return All datasource values for all datasources. The first index is the index of the datasource,
|
||||
* the second index is the index of the datasource value. The number of datasource values is equal
|
||||
* to the number of timestamps returned with {@link #getTimestamps()} method.
|
||||
* @throws RrdException Thrown if invalid datasource name is specified,
|
||||
* or if datasource values are not yet calculated (method {@link #processData()}
|
||||
* was not called)
|
||||
*/
|
||||
public double[][] getValues() throws RrdException {
|
||||
String[] names = getSourceNames();
|
||||
double[][] values = new double[names.length][];
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
values[i] = getValues(names[i]);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private Source getSource(String sourceName) throws RrdException {
|
||||
Source source = sources.get(sourceName);
|
||||
if (source != null) {
|
||||
return source;
|
||||
}
|
||||
throw new RrdException("Unknown source: " + sourceName);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DATASOURCE DEFINITIONS
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Adds a custom, {@link org.jrobin.data.Plottable plottable} datasource (<b>PDEF</b>).
|
||||
* The datapoints should be made available by a class extending
|
||||
* {@link org.jrobin.data.Plottable Plottable} class.
|
||||
* <p>
|
||||
*
|
||||
* @param name source name.
|
||||
* @param plottable class that extends Plottable class and is suited for graphing.
|
||||
*/
|
||||
public void addDatasource(String name, Plottable plottable) {
|
||||
PDef pDef = new PDef(name, plottable);
|
||||
sources.put(name, pDef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds complex source (<b>CDEF</b>).
|
||||
* Complex sources are evaluated using the supplied <code>RPN</code> expression.
|
||||
* <p>
|
||||
* Complex source <code>name</code> can be used:
|
||||
* <ul>
|
||||
* <li>To specify sources for line, area and stack plots.</li>
|
||||
* <li>To define other complex sources.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* JRobin supports the following RPN functions, operators and constants: +, -, *, /,
|
||||
* %, SIN, COS, LOG, EXP, FLOOR, CEIL, ROUND, POW, ABS, SQRT, RANDOM, LT, LE, GT, GE, EQ,
|
||||
* IF, MIN, MAX, LIMIT, DUP, EXC, POP, UN, UNKN, NOW, TIME, PI, E,
|
||||
* AND, OR, XOR, PREV, PREV(sourceName), INF, NEGINF, STEP, YEAR, MONTH, DATE,
|
||||
* HOUR, MINUTE, SECOND, WEEK, SIGN and RND.
|
||||
* <p>
|
||||
* JRobin does not force you to specify at least one simple source name as RRDTool.
|
||||
* <p>
|
||||
* For more details on RPN see RRDTool's
|
||||
* <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/manual/rrdgraph.html" target="man">
|
||||
* rrdgraph man page</a>.
|
||||
*
|
||||
* @param name source name.
|
||||
* @param rpnExpression RPN expression containig comma (or space) delimited simple and complex
|
||||
* source names, RPN constants, functions and operators.
|
||||
*/
|
||||
public void addDatasource(String name, String rpnExpression) {
|
||||
CDef cDef = new CDef(name, rpnExpression);
|
||||
sources.put(name, cDef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds static source (<b>SDEF</b>). Static sources are the result of a consolidation function applied
|
||||
* to *any* other source that has been defined previously.
|
||||
*
|
||||
* @param name source name.
|
||||
* @param defName Name of the datasource to calculate the value from.
|
||||
* @param consolFun Consolidation function to use for value calculation
|
||||
*/
|
||||
public void addDatasource(String name, String defName, String consolFun) {
|
||||
SDef sDef = new SDef(name, defName, consolFun);
|
||||
sources.put(name, sDef);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds simple datasource (<b>DEF</b>). Simple source <code>name</code>
|
||||
* can be used:</p>
|
||||
* <ul>
|
||||
* <li>To specify sources for line, area and stack plots.</li>
|
||||
* <li>To define complex sources
|
||||
* </ul>
|
||||
*
|
||||
* @param name source name.
|
||||
* @param file Path to RRD file.
|
||||
* @param dsName Datasource name defined in the RRD file.
|
||||
* @param consolFunc Consolidation function that will be used to extract data from the RRD
|
||||
* file ("AVERAGE", "MIN", "MAX" or "LAST" - these string constants are conveniently defined
|
||||
* in the {@link org.jrobin.core.ConsolFuns ConsolFuns} class).
|
||||
*/
|
||||
public void addDatasource(String name, String file, String dsName, String consolFunc) {
|
||||
Def def = new Def(name, file, dsName, consolFunc);
|
||||
sources.put(name, def);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds simple source (<b>DEF</b>). Source <code>name</code> can be used:</p>
|
||||
* <ul>
|
||||
* <li>To specify sources for line, area and stack plots.</li>
|
||||
* <li>To define complex sources
|
||||
* </ul>
|
||||
*
|
||||
* @param name Source name.
|
||||
* @param file Path to RRD file.
|
||||
* @param dsName Data source name defined in the RRD file.
|
||||
* @param consolFunc Consolidation function that will be used to extract data from the RRD
|
||||
* file ("AVERAGE", "MIN", "MAX" or "LAST" - these string constants are conveniently defined
|
||||
* in the {@link org.jrobin.core.ConsolFuns ConsolFuns} class).
|
||||
* @param backend Name of the RrdBackendFactory that should be used for this RrdDb.
|
||||
*/
|
||||
public void addDatasource(String name, String file, String dsName, String consolFunc, String backend) {
|
||||
Def def = new Def(name, file, dsName, consolFunc, backend);
|
||||
sources.put(name, def);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds DEF datasource with datasource values already available in the FetchData object. This method is
|
||||
* used internally by JRobin and probably has no purpose outside of it.
|
||||
*
|
||||
* @param name Source name.
|
||||
* @param fetchData Fetched data containing values for the given source name.
|
||||
*/
|
||||
public void addDatasource(String name, FetchData fetchData) {
|
||||
Def def = new Def(name, fetchData);
|
||||
sources.put(name, def);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new VDEF datasource that performs a percentile calculation on an
|
||||
* another named datasource to yield a single value.
|
||||
*
|
||||
* Requires that the other datasource has already been defined; otherwise, it'll
|
||||
* end up with no data
|
||||
*
|
||||
* @param name - the new virtual datasource name
|
||||
* @param sourceName - the datasource from which to extract the percentile. Must be a previously
|
||||
* defined virtual datasource
|
||||
* @param percentile - the percentile to extract from the source datasource
|
||||
*/
|
||||
public void addDatasource(String name, String sourceName, double percentile) {
|
||||
Source source = sources.get(sourceName);
|
||||
sources.put(name, new PercentileDef(name, source, percentile));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new VDEF datasource that performs a percentile calculation on an
|
||||
* another named datasource to yield a single value.
|
||||
*
|
||||
* Requires that the other datasource has already been defined; otherwise, it'll
|
||||
* end up with no data
|
||||
*
|
||||
* @param name - the new virtual datasource name
|
||||
* @param sourceName - the datasource from which to extract the percentile. Must be a previously
|
||||
* defined virtual datasource
|
||||
* @param percentile - the percentile to extract from the source datasource
|
||||
* @param ignorenan - true if we include Double.NaN
|
||||
*/
|
||||
public void addDatasource(String name, String sourceName, double percentile, boolean ignorenan) {
|
||||
Source source = sources.get(sourceName);
|
||||
sources.put(name, new PercentileDef(name, source, percentile, ignorenan));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// CALCULATIONS
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Method that should be called once all datasources are defined. Data will be fetched from
|
||||
* RRD files, RPN expressions will be calculated, etc.
|
||||
*
|
||||
* @throws IOException Thrown in case of I/O error (while fetching data from RRD files)
|
||||
* @throws RrdException Thrown in case of JRobin specific error
|
||||
*/
|
||||
public void processData() throws IOException, RrdException {
|
||||
extractDefs();
|
||||
fetchRrdData();
|
||||
fixZeroEndingTimestamp();
|
||||
chooseOptimalStep();
|
||||
createTimestamps();
|
||||
assignTimestampsToSources();
|
||||
normalizeRrdValues();
|
||||
calculateNonRrdSources();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to calculate datasource values which should be presented on the graph
|
||||
* based on the desired graph width. Each value returned represents a single pixel on the graph.
|
||||
* Corresponding timestamp can be found in the array returned from {@link #getTimestampsPerPixel()}
|
||||
* method.
|
||||
*
|
||||
* @param sourceName Datasource name
|
||||
* @param pixelCount Graph width
|
||||
* @return Per-pixel datasource values
|
||||
* @throws RrdException Thrown if datasource values are not yet calculated (method {@link #processData()}
|
||||
* was not called)
|
||||
*/
|
||||
public double[] getValuesPerPixel(String sourceName, int pixelCount) throws RrdException {
|
||||
setPixelCount(pixelCount);
|
||||
return getValuesPerPixel(sourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to calculate datasource values which should be presented on the graph
|
||||
* based on the graph width set with a {@link #setPixelCount(int)} method call.
|
||||
* Each value returned represents a single pixel on the graph. Corresponding timestamp can be
|
||||
* found in the array returned from {@link #getTimestampsPerPixel()} method.
|
||||
*
|
||||
* @param sourceName Datasource name
|
||||
* @return Per-pixel datasource values
|
||||
* @throws RrdException Thrown if datasource values are not yet calculated (method {@link #processData()}
|
||||
* was not called)
|
||||
*/
|
||||
public double[] getValuesPerPixel(String sourceName) throws RrdException {
|
||||
double[] values = getValues(sourceName);
|
||||
double[] pixelValues = new double[pixelCount];
|
||||
Arrays.fill(pixelValues, Double.NaN);
|
||||
long span = tEnd - tStart;
|
||||
// this is the ugliest nested loop I have ever made
|
||||
for (int pix = 0, ref = 0; pix < pixelCount; pix++) {
|
||||
double t = tStart + (double) (span * pix) / (double) (pixelCount - 1);
|
||||
while (ref < timestamps.length) {
|
||||
if (t <= timestamps[ref] - step) {
|
||||
// too left, nothing to do, already NaN
|
||||
break;
|
||||
}
|
||||
else if (t <= timestamps[ref]) {
|
||||
// in brackets, get this value
|
||||
pixelValues[pix] = values[ref];
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// too right
|
||||
ref++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pixelValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates timestamps which correspond to individual pixels on the graph.
|
||||
*
|
||||
* @param pixelCount Graph width
|
||||
* @return Array of timestamps
|
||||
*/
|
||||
public long[] getTimestampsPerPixel(int pixelCount) {
|
||||
setPixelCount(pixelCount);
|
||||
return getTimestampsPerPixel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates timestamps which correspond to individual pixels on the graph
|
||||
* based on the graph width set with a {@link #setPixelCount(int)} method call.
|
||||
*
|
||||
* @return Array of timestamps
|
||||
*/
|
||||
public long[] getTimestampsPerPixel() {
|
||||
long[] times = new long[pixelCount];
|
||||
long span = tEnd - tStart;
|
||||
for (int i = 0; i < pixelCount; i++) {
|
||||
times[i] = Math.round(tStart + (double) (span * i) / (double) (pixelCount - 1));
|
||||
}
|
||||
return times;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps timestamps and values of all datasources in a tabelar form. Very useful for debugging.
|
||||
*
|
||||
* @return Dumped object content.
|
||||
* @throws RrdException Thrown if nothing is calculated so far (the method {@link #processData()}
|
||||
* was not called).
|
||||
*/
|
||||
public String dump() throws RrdException {
|
||||
String[] names = getSourceNames();
|
||||
double[][] values = getValues();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(format("timestamp", 12));
|
||||
for (String name : names) {
|
||||
buffer.append(format(name, 20));
|
||||
}
|
||||
buffer.append("\n");
|
||||
for (int i = 0; i < timestamps.length; i++) {
|
||||
buffer.append(format("" + timestamps[i], 12));
|
||||
for (int j = 0; j < names.length; j++) {
|
||||
buffer.append(format(Util.formatDouble(values[j][i]), 20));
|
||||
}
|
||||
buffer.append("\n");
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns time when last RRD archive was updated (all RRD files are considered).
|
||||
*
|
||||
* @return Last archive update time for all RRD files in this DataProcessor
|
||||
*/
|
||||
public long getLastRrdArchiveUpdateTime() {
|
||||
return lastRrdArchiveUpdateTime;
|
||||
}
|
||||
|
||||
// PRIVATE METHODS
|
||||
|
||||
private void extractDefs() {
|
||||
List<Def> defList = new ArrayList<Def>();
|
||||
for (Source source : sources.values()) {
|
||||
if (source instanceof Def) {
|
||||
defList.add((Def) source);
|
||||
}
|
||||
}
|
||||
defSources = defList.toArray(new Def[defList.size()]);
|
||||
}
|
||||
|
||||
private void fetchRrdData() throws IOException, RrdException {
|
||||
long tEndFixed = (tEnd == 0) ? Util.getTime() : tEnd;
|
||||
for (int i = 0; i < defSources.length; i++) {
|
||||
if (!defSources[i].isLoaded()) {
|
||||
// not fetched yet
|
||||
Set<String> dsNames = new HashSet<String>();
|
||||
dsNames.add(defSources[i].getDsName());
|
||||
// look for all other datasources with the same path and the same consolidation function
|
||||
for (int j = i + 1; j < defSources.length; j++) {
|
||||
if (defSources[i].isCompatibleWith(defSources[j])) {
|
||||
dsNames.add(defSources[j].getDsName());
|
||||
}
|
||||
}
|
||||
// now we have everything
|
||||
RrdDb rrd = null;
|
||||
try {
|
||||
rrd = getRrd(defSources[i]);
|
||||
lastRrdArchiveUpdateTime = Math.max(lastRrdArchiveUpdateTime, rrd.getLastArchiveUpdateTime());
|
||||
FetchRequest req = rrd.createFetchRequest(defSources[i].getConsolFun(),
|
||||
tStart, tEndFixed, fetchRequestResolution);
|
||||
req.setFilter(dsNames);
|
||||
FetchData data = req.fetchData();
|
||||
defSources[i].setFetchData(data);
|
||||
for (int j = i + 1; j < defSources.length; j++) {
|
||||
if (defSources[i].isCompatibleWith(defSources[j])) {
|
||||
defSources[j].setFetchData(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (rrd != null) {
|
||||
releaseRrd(rrd, defSources[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fixZeroEndingTimestamp() throws RrdException {
|
||||
if (tEnd == 0) {
|
||||
if (defSources.length == 0) {
|
||||
throw new RrdException("Could not adjust zero ending timestamp, no DEF source provided");
|
||||
}
|
||||
tEnd = defSources[0].getArchiveEndTime();
|
||||
for (int i = 1; i < defSources.length; i++) {
|
||||
tEnd = Math.min(tEnd, defSources[i].getArchiveEndTime());
|
||||
}
|
||||
if (tEnd <= tStart) {
|
||||
throw new RrdException("Could not resolve zero ending timestamp.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tricky and ugly. Should be redesigned some time in the future
|
||||
private void chooseOptimalStep() {
|
||||
long newStep = Long.MAX_VALUE;
|
||||
for (Def defSource : defSources) {
|
||||
long fetchStep = defSource.getFetchStep(), tryStep = fetchStep;
|
||||
if (step > 0) {
|
||||
tryStep = Math.min(newStep, (((step - 1) / fetchStep) + 1) * fetchStep);
|
||||
}
|
||||
newStep = Math.min(newStep, tryStep);
|
||||
}
|
||||
if (newStep != Long.MAX_VALUE) {
|
||||
// step resolved from a RRD file
|
||||
step = newStep;
|
||||
}
|
||||
else {
|
||||
// choose step based on the number of pixels (useful for plottable datasources)
|
||||
step = Math.max((tEnd - tStart) / pixelCount, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void createTimestamps() {
|
||||
long t1 = Util.normalize(tStart, step);
|
||||
long t2 = Util.normalize(tEnd, step);
|
||||
if (t2 < tEnd) {
|
||||
t2 += step;
|
||||
}
|
||||
int count = (int) (((t2 - t1) / step) + 1);
|
||||
timestamps = new long[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
timestamps[i] = t1;
|
||||
t1 += step;
|
||||
}
|
||||
}
|
||||
|
||||
private void assignTimestampsToSources() {
|
||||
for (Source src : sources.values()) {
|
||||
src.setTimestamps(timestamps);
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeRrdValues() throws RrdException {
|
||||
Normalizer normalizer = new Normalizer(timestamps);
|
||||
for (Def def : defSources) {
|
||||
long[] rrdTimestamps = def.getRrdTimestamps();
|
||||
double[] rrdValues = def.getRrdValues();
|
||||
double[] values = normalizer.normalize(rrdTimestamps, rrdValues);
|
||||
def.setValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateNonRrdSources() throws RrdException {
|
||||
for (Source source : sources.values()) {
|
||||
if (source instanceof SDef) {
|
||||
calculateSDef((SDef) source);
|
||||
}
|
||||
else if (source instanceof CDef) {
|
||||
calculateCDef((CDef) source);
|
||||
}
|
||||
else if (source instanceof PDef) {
|
||||
calculatePDef((PDef) source);
|
||||
}
|
||||
else if (source instanceof PercentileDef) {
|
||||
calculatePercentileDef((PercentileDef) source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void calculatePDef(PDef pdef) {
|
||||
pdef.calculateValues();
|
||||
}
|
||||
|
||||
private void calculateCDef(CDef cDef) throws RrdException {
|
||||
RpnCalculator calc = new RpnCalculator(cDef.getRpnExpression(), cDef.getName(), this);
|
||||
cDef.setValues(calc.calculateValues());
|
||||
}
|
||||
|
||||
private void calculateSDef(SDef sDef) throws RrdException {
|
||||
String defName = sDef.getDefName();
|
||||
String consolFun = sDef.getConsolFun();
|
||||
Source source = getSource(defName);
|
||||
if (consolFun.equals("MAXIMUM")) { consolFun = "MAX"; }
|
||||
else if (consolFun.equals("MINIMUM")) { consolFun = "MIN"; }
|
||||
double value = source.getAggregates(tStart, tEnd).getAggregate(consolFun);
|
||||
sDef.setValue(value);
|
||||
}
|
||||
|
||||
//Yeah, this is different from the other calculation methods
|
||||
// Frankly, this is how it *should* be done, and the other methods will
|
||||
// be refactored to this design (and the instanceof's removed) at some point
|
||||
private void calculatePercentileDef(PercentileDef def) throws RrdException {
|
||||
def.calculate(tStart, tEnd);
|
||||
}
|
||||
|
||||
|
||||
private RrdDb getRrd(Def def) throws IOException, RrdException {
|
||||
String path = def.getPath(), backend = def.getBackend();
|
||||
if (poolUsed && backend == null) {
|
||||
return RrdDbPool.getInstance().requestRrdDb(path);
|
||||
}
|
||||
else if (backend != null) {
|
||||
return new RrdDb(path, true, RrdBackendFactory.getFactory(backend));
|
||||
}
|
||||
else {
|
||||
return new RrdDb(path, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseRrd(RrdDb rrd, Def def) throws IOException, RrdException {
|
||||
String backend = def.getBackend();
|
||||
if (poolUsed && backend == null) {
|
||||
RrdDbPool.getInstance().release(rrd);
|
||||
}
|
||||
else {
|
||||
rrd.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static String format(String s, int length) {
|
||||
StringBuilder b = new StringBuilder(s);
|
||||
for (int i = 0; i < length - s.length(); i++) {
|
||||
b.append(' ');
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cute little demo. Uses demo.rrd file previously created by basic JRobin demo.
|
||||
*
|
||||
* @param args Not used
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @throws RrdException Thrown if internal jrobin error occurs.
|
||||
*/
|
||||
public static void main(String[] args) throws IOException, RrdException {
|
||||
// time span
|
||||
long t1 = Util.getTimestamp(2003, 4, 1);
|
||||
long t2 = Util.getTimestamp(2003, 5, 1);
|
||||
System.out.println("t1 = " + t1);
|
||||
System.out.println("t2 = " + t2);
|
||||
|
||||
// RRD file to use
|
||||
String rrdPath = Util.getJRobinDemoPath("demo.rrd");
|
||||
|
||||
// constructor
|
||||
DataProcessor dp = new DataProcessor(t1, t2);
|
||||
|
||||
// uncomment and run again
|
||||
//dp.setFetchRequestResolution(86400);
|
||||
|
||||
// uncomment and run again
|
||||
//dp.setStep(86500);
|
||||
|
||||
// datasource definitions
|
||||
dp.addDatasource("X", rrdPath, "sun", "AVERAGE");
|
||||
dp.addDatasource("Y", rrdPath, "shade", "AVERAGE");
|
||||
dp.addDatasource("Z", "X,Y,+,2,/");
|
||||
dp.addDatasource("DERIVE[Z]", "Z,PREV(Z),-,STEP,/");
|
||||
dp.addDatasource("TREND[Z]", "DERIVE[Z],SIGN");
|
||||
dp.addDatasource("AVG[Z]", "Z", "AVERAGE");
|
||||
dp.addDatasource("DELTA", "Z,AVG[Z],-");
|
||||
|
||||
// action
|
||||
long laptime = System.currentTimeMillis();
|
||||
//dp.setStep(86400);
|
||||
dp.processData();
|
||||
System.out.println("Data processed in " + (System.currentTimeMillis() - laptime) + " milliseconds\n---");
|
||||
System.out.println(dp.dump());
|
||||
|
||||
// aggregates
|
||||
System.out.println("\nAggregates for X");
|
||||
Aggregates agg = dp.getAggregates("X");
|
||||
System.out.println(agg.dump());
|
||||
System.out.println("\nAggregates for Y");
|
||||
agg = dp.getAggregates("Y");
|
||||
System.out.println(agg.dump());
|
||||
|
||||
// 95-percentile
|
||||
System.out.println("\n95-percentile for X: " + Util.formatDouble(dp.get95Percentile("X")));
|
||||
System.out.println("95-percentile for Y: " + Util.formatDouble(dp.get95Percentile("Y")));
|
||||
|
||||
// lastArchiveUpdateTime
|
||||
System.out.println("\nLast archive update time was: " + dp.getLastRrdArchiveUpdateTime());
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.FetchData;
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class Def extends Source {
|
||||
private String path, dsName, consolFun, backend;
|
||||
private FetchData fetchData;
|
||||
|
||||
Def(String name, FetchData fetchData) {
|
||||
this(name, null, name, null, null);
|
||||
setFetchData(fetchData);
|
||||
}
|
||||
|
||||
Def(String name, String path, String dsName, String consolFunc) {
|
||||
this(name, path, dsName, consolFunc, null);
|
||||
}
|
||||
|
||||
Def(String name, String path, String dsName, String consolFunc, String backend) {
|
||||
super(name);
|
||||
this.path = path;
|
||||
this.dsName = dsName;
|
||||
this.consolFun = consolFunc;
|
||||
this.backend = backend;
|
||||
}
|
||||
|
||||
String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
String getCanonicalPath() throws IOException {
|
||||
return Util.getCanonicalPath(path);
|
||||
}
|
||||
|
||||
String getDsName() {
|
||||
return dsName;
|
||||
}
|
||||
|
||||
String getConsolFun() {
|
||||
return consolFun;
|
||||
}
|
||||
|
||||
String getBackend() {
|
||||
return backend;
|
||||
}
|
||||
|
||||
boolean isCompatibleWith(Def def) throws IOException {
|
||||
return getCanonicalPath().equals(def.getCanonicalPath()) &&
|
||||
getConsolFun().equals(def.consolFun) &&
|
||||
((backend == null && def.backend == null) ||
|
||||
(backend != null && def.backend != null && backend.equals(def.backend)));
|
||||
}
|
||||
|
||||
void setFetchData(FetchData fetchData) {
|
||||
this.fetchData = fetchData;
|
||||
}
|
||||
|
||||
long[] getRrdTimestamps() {
|
||||
return fetchData.getTimestamps();
|
||||
}
|
||||
|
||||
double[] getRrdValues() throws RrdException {
|
||||
return fetchData.getValues(dsName);
|
||||
}
|
||||
|
||||
long getArchiveEndTime() {
|
||||
return fetchData.getArcEndTime();
|
||||
}
|
||||
|
||||
long getFetchStep() {
|
||||
return fetchData.getStep();
|
||||
}
|
||||
|
||||
Aggregates getAggregates(long tStart, long tEnd) throws RrdException {
|
||||
long[] t = getRrdTimestamps();
|
||||
double[] v = getRrdValues();
|
||||
Aggregator agg = new Aggregator(t, v);
|
||||
return agg.getAggregates(tStart, tEnd);
|
||||
}
|
||||
|
||||
double getPercentile(long tStart, long tEnd, double percentile) throws RrdException {
|
||||
long[] t = getRrdTimestamps();
|
||||
double[] v = getRrdValues();
|
||||
Aggregator agg = new Aggregator(t, v);
|
||||
return agg.getPercentile(tStart, tEnd, percentile);
|
||||
}
|
||||
|
||||
boolean isLoaded() {
|
||||
return fetchData != null;
|
||||
}
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Class used to interpolate datasource values from the collection of (timestamp, values)
|
||||
* points. This class is suitable for linear interpolation only.
|
||||
* <p>
|
||||
* Interpolation algorithm returns different values based on the value passed to
|
||||
* {@link #setInterpolationMethod(int) setInterpolationMethod()}. If not set, interpolation
|
||||
* method defaults to standard linear interpolation ({@link #INTERPOLATE_LINEAR}).
|
||||
* Interpolation method handles NaN datasource
|
||||
* values gracefully.
|
||||
*/
|
||||
public class LinearInterpolator extends Plottable {
|
||||
/**
|
||||
* constant used to specify LEFT interpolation.
|
||||
* See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
|
||||
*/
|
||||
public static final int INTERPOLATE_LEFT = 0;
|
||||
/**
|
||||
* constant used to specify RIGHT interpolation.
|
||||
* See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
|
||||
*/
|
||||
public static final int INTERPOLATE_RIGHT = 1;
|
||||
/**
|
||||
* constant used to specify LINEAR interpolation (default interpolation method).
|
||||
* See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
|
||||
*/
|
||||
public static final int INTERPOLATE_LINEAR = 2;
|
||||
/**
|
||||
* constant used to specify LINEAR REGRESSION as interpolation method.
|
||||
* See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
|
||||
*/
|
||||
public static final int INTERPOLATE_REGRESSION = 3;
|
||||
|
||||
private int lastIndexUsed = 0;
|
||||
private int interpolationMethod = INTERPOLATE_LINEAR;
|
||||
|
||||
private long[] timestamps;
|
||||
private double[] values;
|
||||
|
||||
// used only if INTERPOLATE_BESTFIT is specified
|
||||
double b0 = Double.NaN, b1 = Double.NaN;
|
||||
|
||||
/**
|
||||
* Creates LinearInterpolator from arrays of timestamps and corresponding datasource values.
|
||||
*
|
||||
* @param timestamps timestamps in seconds
|
||||
* @param values corresponding datasource values
|
||||
* @throws RrdException Thrown if supplied arrays do not contain at least two values, or if
|
||||
* timestamps are not ordered, or array lengths are not equal.
|
||||
*/
|
||||
public LinearInterpolator(long[] timestamps, double[] values) throws RrdException {
|
||||
this.timestamps = timestamps;
|
||||
this.values = values;
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates LinearInterpolator from arrays of timestamps and corresponding datasource values.
|
||||
*
|
||||
* @param dates Array of Date objects
|
||||
* @param values corresponding datasource values
|
||||
* @throws RrdException Thrown if supplied arrays do not contain at least two values, or if
|
||||
* timestamps are not ordered, or array lengths are not equal.
|
||||
*/
|
||||
public LinearInterpolator(Date[] dates, double[] values) throws RrdException {
|
||||
this.values = values;
|
||||
timestamps = new long[dates.length];
|
||||
for (int i = 0; i < dates.length; i++) {
|
||||
timestamps[i] = Util.getTimestamp(dates[i]);
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates LinearInterpolator from arrays of timestamps and corresponding datasource values.
|
||||
*
|
||||
* @param dates array of GregorianCalendar objects
|
||||
* @param values corresponding datasource values
|
||||
* @throws RrdException Thrown if supplied arrays do not contain at least two values, or if
|
||||
* timestamps are not ordered, or array lengths are not equal.
|
||||
*/
|
||||
public LinearInterpolator(Calendar[] dates, double[] values) throws RrdException {
|
||||
this.values = values;
|
||||
timestamps = new long[dates.length];
|
||||
for (int i = 0; i < dates.length; i++) {
|
||||
timestamps[i] = Util.getTimestamp(dates[i]);
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
private void validate() throws RrdException {
|
||||
boolean ok = true;
|
||||
if (timestamps.length != values.length || timestamps.length < 2) {
|
||||
ok = false;
|
||||
}
|
||||
for (int i = 0; i < timestamps.length - 1 && ok; i++) {
|
||||
if (timestamps[i] >= timestamps[i + 1]) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
throw new RrdException("Invalid plottable data supplied");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets interpolation method to be used. Suppose that we have two timestamp/value pairs:<br>
|
||||
* <code>(t, 100)</code> and <code>(t + 100, 300)</code>. Here are the results interpolator
|
||||
* returns for t + 50 seconds, for various <code>interpolationMethods</code>:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li><code>INTERPOLATE_LEFT: 100</code>
|
||||
* <li><code>INTERPOLATE_RIGHT: 300</code>
|
||||
* <li><code>INTERPOLATE_LINEAR: 200</code>
|
||||
* </ul>
|
||||
* If not set, interpolation method defaults to <code>INTERPOLATE_LINEAR</code>.
|
||||
* <p>
|
||||
* The fourth available interpolation method is INTERPOLATE_REGRESSION. This method uses
|
||||
* simple linear regression to interpolate supplied data with a simple straight line which does not
|
||||
* necessarily pass through all data points. The slope of the best-fit line will be chosen so that the
|
||||
* total square distance of real data points from from the best-fit line is at minimum.
|
||||
* <p>
|
||||
* The full explanation of this inteprolation method can be found
|
||||
* <a href="http://www.tufts.edu/~gdallal/slr.htm">here</a>.
|
||||
*
|
||||
* @param interpolationMethod Should be <code>INTERPOLATE_LEFT</code>,
|
||||
* <code>INTERPOLATE_RIGHT</code>, <code>INTERPOLATE_LINEAR</code> or
|
||||
* <code>INTERPOLATE_REGRESSION</code>. Any other value will be interpreted as
|
||||
* INTERPOLATE_LINEAR (default).
|
||||
*/
|
||||
public void setInterpolationMethod(int interpolationMethod) {
|
||||
switch (interpolationMethod) {
|
||||
case INTERPOLATE_REGRESSION:
|
||||
calculateBestFitLine();
|
||||
this.interpolationMethod = interpolationMethod;
|
||||
break;
|
||||
case INTERPOLATE_LEFT:
|
||||
case INTERPOLATE_RIGHT:
|
||||
case INTERPOLATE_LINEAR:
|
||||
this.interpolationMethod = interpolationMethod;
|
||||
break;
|
||||
default:
|
||||
this.interpolationMethod = INTERPOLATE_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateBestFitLine() {
|
||||
int count = timestamps.length, validCount = 0;
|
||||
double ts = 0.0, vs = 0.0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!Double.isNaN(values[i])) {
|
||||
ts += timestamps[i];
|
||||
vs += values[i];
|
||||
validCount++;
|
||||
}
|
||||
}
|
||||
if (validCount <= 1) {
|
||||
// just one not-NaN point
|
||||
b0 = b1 = Double.NaN;
|
||||
return;
|
||||
}
|
||||
ts /= validCount;
|
||||
vs /= validCount;
|
||||
double s1 = 0, s2 = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!Double.isNaN(values[i])) {
|
||||
double dt = timestamps[i] - ts;
|
||||
double dv = values[i] - vs;
|
||||
s1 += dt * dv;
|
||||
s2 += dt * dt;
|
||||
}
|
||||
}
|
||||
b1 = s1 / s2;
|
||||
b0 = vs - b1 * ts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method overriden from the base class. This method will be called by the framework. Call
|
||||
* this method only if you need interpolated values in your code.
|
||||
*
|
||||
* @param timestamp timestamp in seconds
|
||||
* @return inteprolated datasource value
|
||||
*/
|
||||
public double getValue(long timestamp) {
|
||||
if (interpolationMethod == INTERPOLATE_REGRESSION) {
|
||||
return b0 + b1 * timestamp;
|
||||
}
|
||||
int count = timestamps.length;
|
||||
// check if out of range
|
||||
if (timestamp < timestamps[0] || timestamp > timestamps[count - 1]) {
|
||||
return Double.NaN;
|
||||
}
|
||||
// find matching segment
|
||||
int startIndex = lastIndexUsed;
|
||||
if (timestamp < timestamps[lastIndexUsed]) {
|
||||
// backward reading, shift to the first timestamp
|
||||
startIndex = 0;
|
||||
}
|
||||
for (int i = startIndex; i < count; i++) {
|
||||
if (timestamps[i] == timestamp) {
|
||||
return values[i];
|
||||
}
|
||||
if (i < count - 1 && timestamps[i] < timestamp && timestamp < timestamps[i + 1]) {
|
||||
// matching segment found
|
||||
lastIndexUsed = i;
|
||||
switch (interpolationMethod) {
|
||||
case INTERPOLATE_LEFT:
|
||||
return values[i];
|
||||
case INTERPOLATE_RIGHT:
|
||||
return values[i + 1];
|
||||
case INTERPOLATE_LINEAR:
|
||||
double slope = (values[i + 1] - values[i]) /
|
||||
(timestamps[i + 1] - timestamps[i]);
|
||||
return values[i] + slope * (timestamp - timestamps[i]);
|
||||
default:
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
// should not be here ever, but let's satisfy the compiler
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class Normalizer {
|
||||
final private long[] timestamps;
|
||||
final int count;
|
||||
final long step;
|
||||
|
||||
Normalizer(long[] timestamps) {
|
||||
this.timestamps = timestamps;
|
||||
this.step = timestamps[1] - timestamps[0];
|
||||
this.count = timestamps.length;
|
||||
}
|
||||
|
||||
double[] normalize(long[] rawTimestamps, double[] rawValues) {
|
||||
int rawCount = rawTimestamps.length;
|
||||
long rawStep = rawTimestamps[1] - rawTimestamps[0];
|
||||
// check if we have a simple match
|
||||
if (rawCount == count && rawStep == step && rawTimestamps[0] == timestamps[0]) {
|
||||
return getCopyOf(rawValues);
|
||||
}
|
||||
// reset all normalized values to NaN
|
||||
double[] values = new double[count];
|
||||
Arrays.fill(values, Double.NaN);
|
||||
for (int rawSeg = 0, seg = 0; rawSeg < rawCount && seg < count; rawSeg++) {
|
||||
double rawValue = rawValues[rawSeg];
|
||||
if (!Double.isNaN(rawValue)) {
|
||||
long rawLeft = rawTimestamps[rawSeg] - rawStep;
|
||||
while (seg < count && rawLeft >= timestamps[seg]) {
|
||||
seg++;
|
||||
}
|
||||
boolean overlap = true;
|
||||
for (int fillSeg = seg; overlap && fillSeg < count; fillSeg++) {
|
||||
long left = timestamps[fillSeg] - step;
|
||||
long t1 = Math.max(rawLeft, left);
|
||||
long t2 = Math.min(rawTimestamps[rawSeg], timestamps[fillSeg]);
|
||||
if (t1 < t2) {
|
||||
values[fillSeg] = Util.sum(values[fillSeg], (t2 - t1) * rawValues[rawSeg]);
|
||||
}
|
||||
else {
|
||||
overlap = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int seg = 0; seg < count; seg++) {
|
||||
values[seg] /= step;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private static double[] getCopyOf(double[] rawValues) {
|
||||
int n = rawValues.length;
|
||||
double[] values = new double[n];
|
||||
System.arraycopy(rawValues, 0, values, 0, n);
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
class PDef extends Source {
|
||||
private final Plottable plottable;
|
||||
|
||||
PDef(String name, Plottable plottable) {
|
||||
super(name);
|
||||
this.plottable = plottable;
|
||||
}
|
||||
|
||||
void calculateValues() {
|
||||
long[] times = getTimestamps();
|
||||
double[] vals = new double[times.length];
|
||||
for (int i = 0; i < times.length; i++) {
|
||||
vals[i] = plottable.getValue(times[i]);
|
||||
}
|
||||
setValues(vals);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2011 Craig Miskell
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
public class PercentileDef extends Source {
|
||||
|
||||
private Source m_source;
|
||||
|
||||
private double m_value;
|
||||
|
||||
private double m_percentile;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private boolean m_ignorenan;
|
||||
|
||||
PercentileDef(String name, Source source, double percentile) {
|
||||
this(name, source, percentile, false);
|
||||
}
|
||||
|
||||
PercentileDef(String name, Source source, double percentile, boolean ignorenan) {
|
||||
super(name);
|
||||
|
||||
m_percentile = percentile;
|
||||
m_ignorenan = ignorenan;
|
||||
m_source = source;
|
||||
|
||||
//The best we can do at this point; until this object has it's value realized over a
|
||||
// particular time period (with calculate()), there's not much else to do
|
||||
this.setValue(Double.NaN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Realize the calculation of this definition, over the given time period
|
||||
*
|
||||
* @param tStart the time period start
|
||||
* @param tEnd the time period end
|
||||
* @throws RrdException Thrown if we cannot get a percentile value for the time period.
|
||||
*/
|
||||
public void calculate(long tStart, long tEnd) throws RrdException {
|
||||
if(m_source != null) {
|
||||
this.setValue(m_source.getPercentile(tStart, tEnd, m_percentile));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the given value and puts it in each position in the 'values' array.
|
||||
* @param value
|
||||
*/
|
||||
private void setValue(double value) {
|
||||
this.m_value = value;
|
||||
long[] times = getTimestamps();
|
||||
if( times != null ) {
|
||||
int count = times.length;
|
||||
double[] values = new double[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
values[i] = m_value;
|
||||
}
|
||||
setValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void setTimestamps(long[] timestamps) {
|
||||
super.setTimestamps(timestamps);
|
||||
//And now also call setValue with the current value, to sort out "values"
|
||||
setValue(m_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as SDef; the aggregates of a static value are all just the
|
||||
* same static value.
|
||||
*
|
||||
* Assumes this def has been realized by calling calculate(), otherwise
|
||||
* the aggregated values will be NaN
|
||||
*/
|
||||
@Override
|
||||
Aggregates getAggregates(long tStart, long tEnd) throws RrdException {
|
||||
Aggregates agg = new Aggregates();
|
||||
agg.first = agg.last = agg.min = agg.max = agg.average = m_value;
|
||||
agg.total = m_value * (tEnd - tStart);
|
||||
return agg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns just the calculated percentile; the "Xth" percentile of a static value is
|
||||
* the static value itself.
|
||||
*
|
||||
* Assumes this def has been realized by calling calculate(), otherwise
|
||||
* the aggregated values will be NaN
|
||||
*/
|
||||
@Override
|
||||
double getPercentile(long tStart, long tEnd, double percentile)
|
||||
throws RrdException {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.data;
|
||||
|
||||
/**
|
||||
* <p>Interface to be used for custom datasources.
|
||||
* If you wish to use a custom datasource in a graph, you should create a class implementing this interface
|
||||
* that represents that datasource, and then pass this class on to the RrdGraphDef.</p>
|
||||
*/
|
||||
public abstract class Plottable {
|
||||
/**
|
||||
* Retrieves datapoint value based on a given timestamp.
|
||||
* Use this method if you only have one series of data in this class.
|
||||
*
|
||||
* @param timestamp Timestamp in seconds for the datapoint.
|
||||
* @return Double value of the datapoint.
|
||||
*/
|
||||
public double getValue(long timestamp) {
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
@@ -1,832 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TimeZone;
|
||||
|
||||
class RpnCalculator {
|
||||
private static final byte TKN_VAR = 0;
|
||||
private static final byte TKN_NUM = 1;
|
||||
private static final byte TKN_PLUS = 2;
|
||||
private static final byte TKN_MINUS = 3;
|
||||
private static final byte TKN_MULT = 4;
|
||||
private static final byte TKN_DIV = 5;
|
||||
private static final byte TKN_MOD = 6;
|
||||
private static final byte TKN_SIN = 7;
|
||||
private static final byte TKN_COS = 8;
|
||||
private static final byte TKN_LOG = 9;
|
||||
private static final byte TKN_EXP = 10;
|
||||
private static final byte TKN_FLOOR = 11;
|
||||
private static final byte TKN_CEIL = 12;
|
||||
private static final byte TKN_ROUND = 13;
|
||||
private static final byte TKN_POW = 14;
|
||||
private static final byte TKN_ABS = 15;
|
||||
private static final byte TKN_SQRT = 16;
|
||||
private static final byte TKN_RANDOM = 17;
|
||||
private static final byte TKN_LT = 18;
|
||||
private static final byte TKN_LE = 19;
|
||||
private static final byte TKN_GT = 20;
|
||||
private static final byte TKN_GE = 21;
|
||||
private static final byte TKN_EQ = 22;
|
||||
private static final byte TKN_IF = 23;
|
||||
private static final byte TKN_MIN = 24;
|
||||
private static final byte TKN_MAX = 25;
|
||||
private static final byte TKN_LIMIT = 26;
|
||||
private static final byte TKN_DUP = 27;
|
||||
private static final byte TKN_EXC = 28;
|
||||
private static final byte TKN_POP = 29;
|
||||
private static final byte TKN_UN = 30;
|
||||
private static final byte TKN_UNKN = 31;
|
||||
private static final byte TKN_NOW = 32;
|
||||
private static final byte TKN_TIME = 33;
|
||||
private static final byte TKN_PI = 34;
|
||||
private static final byte TKN_E = 35;
|
||||
private static final byte TKN_AND = 36;
|
||||
private static final byte TKN_OR = 37;
|
||||
private static final byte TKN_XOR = 38;
|
||||
private static final byte TKN_PREV = 39;
|
||||
private static final byte TKN_INF = 40;
|
||||
private static final byte TKN_NEGINF = 41;
|
||||
private static final byte TKN_STEP = 42;
|
||||
private static final byte TKN_YEAR = 43;
|
||||
private static final byte TKN_MONTH = 44;
|
||||
private static final byte TKN_DATE = 45;
|
||||
private static final byte TKN_HOUR = 46;
|
||||
private static final byte TKN_MINUTE = 47;
|
||||
private static final byte TKN_SECOND = 48;
|
||||
private static final byte TKN_WEEK = 49;
|
||||
private static final byte TKN_SIGN = 50;
|
||||
private static final byte TKN_RND = 51;
|
||||
private static final byte TKN_ADDNAN = 52;
|
||||
private static final byte TKN_NE = 53;
|
||||
private static final byte TKN_ISINF = 54;
|
||||
private static final byte TKN_ATAN = 55;
|
||||
private static final byte TKN_ATAN2 = 56;
|
||||
private static final byte TKN_DEG2RAD = 57;
|
||||
private static final byte TKN_RAD2DEG = 58;
|
||||
private static final byte TKN_COUNT = 59;
|
||||
private static final byte TKN_SORT = 60;
|
||||
private static final byte TKN_REV = 61;
|
||||
private static final byte TKN_AVG = 62;
|
||||
private static final byte TKN_LTIME = 63;
|
||||
private static final byte TKN_TREND = 64;
|
||||
private static final byte TKN_TRENDNAN = 65;
|
||||
private static final byte TKN_PREDICT = 66;
|
||||
private static final byte TKN_PREDICTSIGMA = 67;
|
||||
|
||||
private String rpnExpression;
|
||||
private String sourceName;
|
||||
private DataProcessor dataProcessor;
|
||||
|
||||
private Token[] tokens;
|
||||
private RpnStack stack = new RpnStack();
|
||||
private double[] calculatedValues;
|
||||
private long[] timestamps;
|
||||
private double timeStep;
|
||||
|
||||
RpnCalculator(String rpnExpression, String sourceName, DataProcessor dataProcessor) throws RrdException {
|
||||
this.rpnExpression = rpnExpression;
|
||||
this.sourceName = sourceName;
|
||||
this.dataProcessor = dataProcessor;
|
||||
this.timestamps = dataProcessor.getTimestamps();
|
||||
this.timeStep = this.timestamps[1] - this.timestamps[0];
|
||||
this.calculatedValues = new double[this.timestamps.length];
|
||||
StringTokenizer st = new StringTokenizer(rpnExpression, ", ");
|
||||
tokens = new Token[st.countTokens()];
|
||||
for (int i = 0; st.hasMoreTokens(); i++) {
|
||||
tokens[i] = createToken(st.nextToken());
|
||||
}
|
||||
}
|
||||
|
||||
private Token createToken(String parsedText) throws RrdException {
|
||||
Token token = new Token();
|
||||
if (Util.isDouble(parsedText)) {
|
||||
token.id = TKN_NUM;
|
||||
token.number = Util.parseDouble(parsedText);
|
||||
}
|
||||
else if (parsedText.equals("+")) {
|
||||
token.id = TKN_PLUS;
|
||||
}
|
||||
else if (parsedText.equals("-")) {
|
||||
token.id = TKN_MINUS;
|
||||
}
|
||||
else if (parsedText.equals("*")) {
|
||||
token.id = TKN_MULT;
|
||||
}
|
||||
else if (parsedText.equals("/")) {
|
||||
token.id = TKN_DIV;
|
||||
}
|
||||
else if (parsedText.equals("%")) {
|
||||
token.id = TKN_MOD;
|
||||
}
|
||||
else if (parsedText.equals("SIN")) {
|
||||
token.id = TKN_SIN;
|
||||
}
|
||||
else if (parsedText.equals("COS")) {
|
||||
token.id = TKN_COS;
|
||||
}
|
||||
else if (parsedText.equals("LOG")) {
|
||||
token.id = TKN_LOG;
|
||||
}
|
||||
else if (parsedText.equals("EXP")) {
|
||||
token.id = TKN_EXP;
|
||||
}
|
||||
else if (parsedText.equals("FLOOR")) {
|
||||
token.id = TKN_FLOOR;
|
||||
}
|
||||
else if (parsedText.equals("CEIL")) {
|
||||
token.id = TKN_CEIL;
|
||||
}
|
||||
else if (parsedText.equals("ROUND")) {
|
||||
token.id = TKN_ROUND;
|
||||
}
|
||||
else if (parsedText.equals("POW")) {
|
||||
token.id = TKN_POW;
|
||||
}
|
||||
else if (parsedText.equals("ABS")) {
|
||||
token.id = TKN_ABS;
|
||||
}
|
||||
else if (parsedText.equals("SQRT")) {
|
||||
token.id = TKN_SQRT;
|
||||
}
|
||||
else if (parsedText.equals("RANDOM")) {
|
||||
token.id = TKN_RANDOM;
|
||||
}
|
||||
else if (parsedText.equals("LT")) {
|
||||
token.id = TKN_LT;
|
||||
}
|
||||
else if (parsedText.equals("LE")) {
|
||||
token.id = TKN_LE;
|
||||
}
|
||||
else if (parsedText.equals("GT")) {
|
||||
token.id = TKN_GT;
|
||||
}
|
||||
else if (parsedText.equals("GE")) {
|
||||
token.id = TKN_GE;
|
||||
}
|
||||
else if (parsedText.equals("EQ")) {
|
||||
token.id = TKN_EQ;
|
||||
}
|
||||
else if (parsedText.equals("IF")) {
|
||||
token.id = TKN_IF;
|
||||
}
|
||||
else if (parsedText.equals("MIN")) {
|
||||
token.id = TKN_MIN;
|
||||
}
|
||||
else if (parsedText.equals("MAX")) {
|
||||
token.id = TKN_MAX;
|
||||
}
|
||||
else if (parsedText.equals("LIMIT")) {
|
||||
token.id = TKN_LIMIT;
|
||||
}
|
||||
else if (parsedText.equals("DUP")) {
|
||||
token.id = TKN_DUP;
|
||||
}
|
||||
else if (parsedText.equals("EXC")) {
|
||||
token.id = TKN_EXC;
|
||||
}
|
||||
else if (parsedText.equals("POP")) {
|
||||
token.id = TKN_POP;
|
||||
}
|
||||
else if (parsedText.equals("UN")) {
|
||||
token.id = TKN_UN;
|
||||
}
|
||||
else if (parsedText.equals("UNKN")) {
|
||||
token.id = TKN_UNKN;
|
||||
}
|
||||
else if (parsedText.equals("NOW")) {
|
||||
token.id = TKN_NOW;
|
||||
}
|
||||
else if (parsedText.equals("TIME")) {
|
||||
token.id = TKN_TIME;
|
||||
}
|
||||
else if (parsedText.equals("LTIME")) {
|
||||
token.id = TKN_LTIME;
|
||||
}
|
||||
else if (parsedText.equals("PI")) {
|
||||
token.id = TKN_PI;
|
||||
}
|
||||
else if (parsedText.equals("E")) {
|
||||
token.id = TKN_E;
|
||||
}
|
||||
else if (parsedText.equals("AND")) {
|
||||
token.id = TKN_AND;
|
||||
}
|
||||
else if (parsedText.equals("OR")) {
|
||||
token.id = TKN_OR;
|
||||
}
|
||||
else if (parsedText.equals("XOR")) {
|
||||
token.id = TKN_XOR;
|
||||
}
|
||||
else if (parsedText.equals("PREV")) {
|
||||
token.id = TKN_PREV;
|
||||
token.variable = sourceName;
|
||||
token.values = calculatedValues;
|
||||
}
|
||||
else if (parsedText.startsWith("PREV(") && parsedText.endsWith(")")) {
|
||||
token.id = TKN_PREV;
|
||||
token.variable = parsedText.substring(5, parsedText.length() - 1);
|
||||
token.values = dataProcessor.getValues(token.variable);
|
||||
}
|
||||
else if (parsedText.equals("INF")) {
|
||||
token.id = TKN_INF;
|
||||
}
|
||||
else if (parsedText.equals("NEGINF")) {
|
||||
token.id = TKN_NEGINF;
|
||||
}
|
||||
else if (parsedText.equals("STEP")) {
|
||||
token.id = TKN_STEP;
|
||||
}
|
||||
else if (parsedText.equals("YEAR")) {
|
||||
token.id = TKN_YEAR;
|
||||
}
|
||||
else if (parsedText.equals("MONTH")) {
|
||||
token.id = TKN_MONTH;
|
||||
}
|
||||
else if (parsedText.equals("DATE")) {
|
||||
token.id = TKN_DATE;
|
||||
}
|
||||
else if (parsedText.equals("HOUR")) {
|
||||
token.id = TKN_HOUR;
|
||||
}
|
||||
else if (parsedText.equals("MINUTE")) {
|
||||
token.id = TKN_MINUTE;
|
||||
}
|
||||
else if (parsedText.equals("SECOND")) {
|
||||
token.id = TKN_SECOND;
|
||||
}
|
||||
else if (parsedText.equals("WEEK")) {
|
||||
token.id = TKN_WEEK;
|
||||
}
|
||||
else if (parsedText.equals("SIGN")) {
|
||||
token.id = TKN_SIGN;
|
||||
}
|
||||
else if (parsedText.equals("RND")) {
|
||||
token.id = TKN_RND;
|
||||
}
|
||||
else if (parsedText.equals("ADDNAN")) {
|
||||
token.id = TKN_ADDNAN;
|
||||
}
|
||||
else if (parsedText.equals("NE")) {
|
||||
token.id = TKN_NE;
|
||||
}
|
||||
else if (parsedText.equals("ISINF")) {
|
||||
token.id = TKN_ISINF;
|
||||
}
|
||||
else if (parsedText.equals("ATAN")) {
|
||||
token.id = TKN_ATAN;
|
||||
}
|
||||
else if (parsedText.equals("ATAN2")) {
|
||||
token.id = TKN_ATAN2;
|
||||
}
|
||||
else if (parsedText.equals("DEG2RAD")) {
|
||||
token.id = TKN_DEG2RAD;
|
||||
}
|
||||
else if (parsedText.equals("RAD2DEG")) {
|
||||
token.id = TKN_RAD2DEG;
|
||||
}
|
||||
else if (parsedText.equals("COUNT")) {
|
||||
token.id = TKN_COUNT;
|
||||
}
|
||||
else if (parsedText.equals("SORT")) {
|
||||
token.id = TKN_SORT;
|
||||
}
|
||||
else if (parsedText.equals("REV")) {
|
||||
token.id = TKN_REV;
|
||||
}
|
||||
else if (parsedText.equals("AVG")) {
|
||||
token.id = TKN_AVG;
|
||||
}
|
||||
else if (parsedText.equals("TREND")) {
|
||||
token.id = TKN_TREND;
|
||||
}
|
||||
else if (parsedText.equals("TRENDNAN")) {
|
||||
token.id = TKN_TRENDNAN;
|
||||
}
|
||||
else if (parsedText.equals("PREDICT")) {
|
||||
token.id = TKN_PREDICT;
|
||||
}
|
||||
else if (parsedText.equals("PREDICTSIGMA")) {
|
||||
token.id = TKN_PREDICTSIGMA;
|
||||
}
|
||||
else {
|
||||
token.id = TKN_VAR;
|
||||
token.variable = parsedText;
|
||||
token.values = dataProcessor.getValues(token.variable);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
double[] calculateValues() throws RrdException {
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
for (int slot = 0; slot < timestamps.length; slot++) {
|
||||
resetStack();
|
||||
int token_rpi = -1;
|
||||
for (int rpi = 0; rpi < tokens.length; rpi++) {
|
||||
Token token = tokens[rpi];
|
||||
double x1, x2, x3;
|
||||
switch (token.id) {
|
||||
case TKN_NUM:
|
||||
push(token.number);
|
||||
break;
|
||||
case TKN_VAR:
|
||||
push(token.values[slot]);
|
||||
token_rpi = rpi;
|
||||
break;
|
||||
case TKN_COUNT:
|
||||
push(slot+1);
|
||||
break;
|
||||
case TKN_PLUS:
|
||||
push(pop() + pop());
|
||||
break;
|
||||
case TKN_MINUS:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 - x2);
|
||||
break;
|
||||
case TKN_MULT:
|
||||
push(pop() * pop());
|
||||
break;
|
||||
case TKN_DIV:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 / x2);
|
||||
break;
|
||||
case TKN_MOD:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 % x2);
|
||||
break;
|
||||
case TKN_SIN:
|
||||
push(Math.sin(pop()));
|
||||
break;
|
||||
case TKN_COS:
|
||||
push(Math.cos(pop()));
|
||||
break;
|
||||
case TKN_ATAN:
|
||||
push(Math.atan(pop()));
|
||||
break;
|
||||
case TKN_ATAN2:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(Math.atan2(x1, x2));
|
||||
break;
|
||||
case TKN_LOG:
|
||||
push(Math.log(pop()));
|
||||
break;
|
||||
case TKN_EXP:
|
||||
push(Math.exp(pop()));
|
||||
break;
|
||||
case TKN_FLOOR:
|
||||
push(Math.floor(pop()));
|
||||
break;
|
||||
case TKN_CEIL:
|
||||
push(Math.ceil(pop()));
|
||||
break;
|
||||
case TKN_ROUND:
|
||||
push(Math.round(pop()));
|
||||
break;
|
||||
case TKN_POW:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(Math.pow(x1, x2));
|
||||
break;
|
||||
case TKN_ABS:
|
||||
push(Math.abs(pop()));
|
||||
break;
|
||||
case TKN_SQRT:
|
||||
push(Math.sqrt(pop()));
|
||||
break;
|
||||
case TKN_RANDOM:
|
||||
push(Math.random());
|
||||
break;
|
||||
case TKN_LT:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 < x2 ? 1 : 0);
|
||||
break;
|
||||
case TKN_LE:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 <= x2 ? 1 : 0);
|
||||
break;
|
||||
case TKN_GT:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 > x2 ? 1 : 0);
|
||||
break;
|
||||
case TKN_GE:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 >= x2 ? 1 : 0);
|
||||
break;
|
||||
case TKN_EQ:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 == x2 ? 1 : 0);
|
||||
break;
|
||||
case TKN_NE:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 != x2 ? 1 : 0);
|
||||
break;
|
||||
case TKN_IF:
|
||||
x3 = pop();
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 != 0 ? x2 : x3);
|
||||
break;
|
||||
case TKN_MIN:
|
||||
push(Math.min(pop(), pop()));
|
||||
break;
|
||||
case TKN_MAX:
|
||||
push(Math.max(pop(), pop()));
|
||||
break;
|
||||
case TKN_LIMIT:
|
||||
x3 = pop();
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x1 < x2 || x1 > x3 ? Double.NaN : x1);
|
||||
break;
|
||||
case TKN_DUP:
|
||||
push(peek());
|
||||
break;
|
||||
case TKN_EXC:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(x2);
|
||||
push(x1);
|
||||
break;
|
||||
case TKN_POP:
|
||||
pop();
|
||||
break;
|
||||
case TKN_UN:
|
||||
push(Double.isNaN(pop()) ? 1 : 0);
|
||||
break;
|
||||
case TKN_ISINF:
|
||||
push(Double.isInfinite(pop()) ? 1 : 0);
|
||||
break;
|
||||
case TKN_UNKN:
|
||||
push(Double.NaN);
|
||||
break;
|
||||
case TKN_NOW:
|
||||
push(Util.getTime());
|
||||
break;
|
||||
case TKN_TIME:
|
||||
push(timestamps[slot]);
|
||||
break;
|
||||
case TKN_LTIME:
|
||||
push(timestamps[slot] + (tz.getOffset(timestamps[slot]) / 1000L));
|
||||
break;
|
||||
case TKN_PI:
|
||||
push(Math.PI);
|
||||
break;
|
||||
case TKN_E:
|
||||
push(Math.E);
|
||||
break;
|
||||
case TKN_AND:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push((x1 != 0 && x2 != 0) ? 1 : 0);
|
||||
break;
|
||||
case TKN_OR:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push((x1 != 0 || x2 != 0) ? 1 : 0);
|
||||
break;
|
||||
case TKN_XOR:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
push(((x1 != 0 && x2 == 0) || (x1 == 0 && x2 != 0)) ? 1 : 0);
|
||||
break;
|
||||
case TKN_PREV:
|
||||
push((slot == 0) ? Double.NaN : token.values[slot - 1]);
|
||||
break;
|
||||
case TKN_INF:
|
||||
push(Double.POSITIVE_INFINITY);
|
||||
break;
|
||||
case TKN_NEGINF:
|
||||
push(Double.NEGATIVE_INFINITY);
|
||||
break;
|
||||
case TKN_STEP:
|
||||
push(timeStep);
|
||||
break;
|
||||
case TKN_YEAR:
|
||||
push(getCalendarField(pop(), Calendar.YEAR));
|
||||
break;
|
||||
case TKN_MONTH:
|
||||
push(getCalendarField(pop(), Calendar.MONTH));
|
||||
break;
|
||||
case TKN_DATE:
|
||||
push(getCalendarField(pop(), Calendar.DAY_OF_MONTH));
|
||||
break;
|
||||
case TKN_HOUR:
|
||||
push(getCalendarField(pop(), Calendar.HOUR_OF_DAY));
|
||||
break;
|
||||
case TKN_MINUTE:
|
||||
push(getCalendarField(pop(), Calendar.MINUTE));
|
||||
break;
|
||||
case TKN_SECOND:
|
||||
push(getCalendarField(pop(), Calendar.SECOND));
|
||||
break;
|
||||
case TKN_WEEK:
|
||||
push(getCalendarField(pop(), Calendar.WEEK_OF_YEAR));
|
||||
break;
|
||||
case TKN_SIGN:
|
||||
x1 = pop();
|
||||
push(Double.isNaN(x1) ? Double.NaN : x1 > 0 ? +1 : x1 < 0 ? -1 : 0);
|
||||
break;
|
||||
case TKN_RND:
|
||||
push(Math.floor(pop() * Math.random()));
|
||||
break;
|
||||
case TKN_ADDNAN:
|
||||
x2 = pop();
|
||||
x1 = pop();
|
||||
if (Double.isNaN(x1)) {
|
||||
push(x2);
|
||||
} else if (Double.isNaN(x2)) {
|
||||
push(x1);
|
||||
} else {
|
||||
push(x1+x2);
|
||||
}
|
||||
break;
|
||||
case TKN_DEG2RAD:
|
||||
push(Math.toRadians(pop()));
|
||||
break;
|
||||
case TKN_RAD2DEG:
|
||||
push(Math.toDegrees(pop()));
|
||||
break;
|
||||
case TKN_SORT:
|
||||
{
|
||||
int n = (int) pop();
|
||||
double[] array = new double[n];
|
||||
for(int i = 0; i < n; i++) {
|
||||
array[i] = pop();
|
||||
}
|
||||
Arrays.sort(array);
|
||||
for (int i = 0; i < n; i++) {
|
||||
push(array[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TKN_REV:
|
||||
{
|
||||
int n = (int) pop();
|
||||
double[] array = new double[n];
|
||||
for(int i = 0; i < n; i++) {
|
||||
array[i] = pop();
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
push(array[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TKN_AVG:
|
||||
{
|
||||
int count = 0;
|
||||
int n = (int) pop();
|
||||
double sum = 0.0;
|
||||
while (n > 0) {
|
||||
x1 = pop();
|
||||
n--;
|
||||
|
||||
if (Double.isNaN(x1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sum += x1;
|
||||
count++;
|
||||
}
|
||||
if (count > 0) {
|
||||
push(sum / count);
|
||||
} else {
|
||||
push(Double.NaN);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TKN_TREND:
|
||||
case TKN_TRENDNAN:
|
||||
{
|
||||
int dur = (int) pop();
|
||||
pop();
|
||||
/*
|
||||
* OK, so to match the output from rrdtool, we have to go *forward* 2 timeperiods.
|
||||
* So at t[59] we use the average of t[1]..t[61]
|
||||
*
|
||||
*/
|
||||
|
||||
if ((slot+1) < Math.ceil(dur / timeStep)) {
|
||||
push(Double.NaN);
|
||||
} else {
|
||||
double[] vals = dataProcessor.getValues(tokens[token_rpi].variable);
|
||||
boolean ignorenan = token.id == TKN_TRENDNAN;
|
||||
double accum = 0.0;
|
||||
int count = 0;
|
||||
|
||||
int start = (int) (Math.ceil(dur / timeStep));
|
||||
int row = 2;
|
||||
while ((slot + row) > vals.length) {
|
||||
row --;
|
||||
}
|
||||
|
||||
for(; start > 0; start--) {
|
||||
double val = vals[slot + row - start];
|
||||
if (ignorenan || !Double.isNaN(val)) {
|
||||
accum = Util.sum(accum, val);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
//System.err.printf("t[%d]: %1.10e / %d\n", slot, (count == 0) ? Double.NaN : (accum / count), count);
|
||||
push((count == 0) ? Double.NaN : (accum / count));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TKN_PREDICT:
|
||||
case TKN_PREDICTSIGMA:
|
||||
{
|
||||
pop(); // Clear the value of our variable
|
||||
|
||||
/* the local averaging window (similar to trend, but better here, as we get better statistics thru numbers)*/
|
||||
int locstepsize = (int) pop();
|
||||
/* the number of shifts and range-checking*/
|
||||
int num_shifts = (int) pop();
|
||||
double[] multipliers;
|
||||
|
||||
// handle negative shifts special
|
||||
if (num_shifts < 0) {
|
||||
multipliers = new double[1];
|
||||
multipliers[0] = pop();
|
||||
} else {
|
||||
multipliers = new double[num_shifts];
|
||||
for(int i = 0; i < num_shifts; i++) {
|
||||
multipliers[i] = pop();
|
||||
}
|
||||
}
|
||||
|
||||
/* the real calculation */
|
||||
double val = Double.NaN;
|
||||
|
||||
/* the info on the datasource */
|
||||
double[] vals = dataProcessor.getValues(tokens[rpi-1].variable);
|
||||
|
||||
int locstep = (int) Math.ceil((float) locstepsize / (float) timeStep);
|
||||
|
||||
/* the sums */
|
||||
double sum = 0;
|
||||
double sum2 = 0;
|
||||
int count = 0;
|
||||
|
||||
/* now loop for each position */
|
||||
int doshifts = Math.abs(num_shifts);
|
||||
for (int loop = 0; loop < doshifts; loop++) {
|
||||
/* calculate shift step */
|
||||
int shiftstep = 1;
|
||||
if (num_shifts < 0) {
|
||||
shiftstep = loop * (int) multipliers[0];
|
||||
} else {
|
||||
shiftstep = (int) multipliers[loop];
|
||||
}
|
||||
if (shiftstep < 0) {
|
||||
throw new RrdException("negative shift step not allowed: " + shiftstep);
|
||||
}
|
||||
shiftstep = (int) Math.ceil((float) shiftstep / (float) timeStep);
|
||||
/* loop all local shifts */
|
||||
for (int i = 0; i <= locstep; i++) {
|
||||
|
||||
int offset = shiftstep + i;
|
||||
if ((offset >= 0) && (offset < slot)) {
|
||||
/* get the value */
|
||||
val = vals[slot - offset];
|
||||
|
||||
/* and handle the non NAN case only*/
|
||||
if (!Double.isNaN(val)) {
|
||||
sum = Util.sum(sum, val);
|
||||
sum2 = Util.sum(sum2, val * val);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* do the final calculations */
|
||||
val = Double.NaN;
|
||||
if (token.id == TKN_PREDICT) { /* the average */
|
||||
if (count > 0) {
|
||||
val = sum / (double) count;
|
||||
}
|
||||
} else {
|
||||
if (count > 1) { /* the sigma case */
|
||||
val = count * sum2 - sum * sum;
|
||||
if (val < 0) {
|
||||
val = Double.NaN;
|
||||
} else {
|
||||
val = Math.sqrt(val / ((float) count * ((float) count - 1.0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
push(val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new RrdException("Unexpected RPN token encountered, token.id=" + token.id);
|
||||
}
|
||||
}
|
||||
calculatedValues[slot] = pop();
|
||||
// check if stack is empty only on the first try
|
||||
if (slot == 0 && !isStackEmpty()) {
|
||||
throw new RrdException("Stack not empty at the end of calculation. " +
|
||||
"Probably bad RPN expression [" + rpnExpression + "]");
|
||||
}
|
||||
}
|
||||
return calculatedValues;
|
||||
}
|
||||
|
||||
private double getCalendarField(double timestamp, int field) {
|
||||
Calendar calendar = Util.getCalendar((long) timestamp);
|
||||
return calendar.get(field);
|
||||
}
|
||||
|
||||
private void push(double x) throws RrdException {
|
||||
stack.push(x);
|
||||
}
|
||||
|
||||
private double pop() throws RrdException {
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
private double peek() throws RrdException {
|
||||
return stack.peek();
|
||||
}
|
||||
|
||||
private void resetStack() {
|
||||
stack.reset();
|
||||
}
|
||||
|
||||
private boolean isStackEmpty() {
|
||||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
private static final class RpnStack {
|
||||
private static final int MAX_STACK_SIZE = 1000;
|
||||
private double[] stack = new double[MAX_STACK_SIZE];
|
||||
private int pos = 0;
|
||||
|
||||
void push(final double x) throws RrdException {
|
||||
if (pos >= MAX_STACK_SIZE) {
|
||||
throw new RrdException("PUSH failed, RPN stack full [" + MAX_STACK_SIZE + "]");
|
||||
}
|
||||
stack[pos++] = x;
|
||||
}
|
||||
|
||||
double pop() throws RrdException {
|
||||
if (pos <= 0) {
|
||||
throw new RrdException("POP failed, RPN stack is empty ");
|
||||
}
|
||||
return stack[--pos];
|
||||
}
|
||||
|
||||
double peek() throws RrdException {
|
||||
if (pos <= 0) {
|
||||
throw new RrdException("PEEK failed, RPN stack is empty ");
|
||||
}
|
||||
return stack[pos - 1];
|
||||
}
|
||||
|
||||
void reset() {
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return pos <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Token {
|
||||
byte id = -1;
|
||||
double number = Double.NaN;
|
||||
String variable = null;
|
||||
double[] values = null;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
class SDef extends Source {
|
||||
private String defName;
|
||||
private String consolFun;
|
||||
private double value;
|
||||
|
||||
SDef(String name, String defName, String consolFun) {
|
||||
super(name);
|
||||
this.defName = defName;
|
||||
this.consolFun = consolFun;
|
||||
}
|
||||
|
||||
String getDefName() {
|
||||
return defName;
|
||||
}
|
||||
|
||||
String getConsolFun() {
|
||||
return consolFun;
|
||||
}
|
||||
|
||||
void setValue(double value) {
|
||||
this.value = value;
|
||||
int count = getTimestamps().length;
|
||||
double[] values = new double[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
values[i] = value;
|
||||
}
|
||||
setValues(values);
|
||||
}
|
||||
|
||||
Aggregates getAggregates(long tStart, long tEnd) throws RrdException {
|
||||
Aggregates agg = new Aggregates();
|
||||
agg.first = agg.last = agg.min = agg.max = agg.average = value;
|
||||
agg.total = value * (tEnd - tStart);
|
||||
return agg;
|
||||
}
|
||||
|
||||
double getPercentile(long tStart, long tEnd, double percentile) throws RrdException {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.data;
|
||||
|
||||
import org.jrobin.core.ConsolFuns;
|
||||
import org.jrobin.core.RrdException;
|
||||
|
||||
abstract class Source implements ConsolFuns {
|
||||
final private String name;
|
||||
protected double[] values;
|
||||
protected long[] timestamps;
|
||||
|
||||
Source(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
void setValues(double[] values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
void setTimestamps(long[] timestamps) {
|
||||
this.timestamps = timestamps;
|
||||
}
|
||||
|
||||
double[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
long[] getTimestamps() {
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
Aggregates getAggregates(long tStart, long tEnd) throws RrdException {
|
||||
Aggregator agg = new Aggregator(timestamps, values);
|
||||
return agg.getAggregates(tStart, tEnd);
|
||||
}
|
||||
|
||||
double getPercentile(long tStart, long tEnd, double percentile) throws RrdException {
|
||||
Aggregator agg = new Aggregator(timestamps, values);
|
||||
return agg.getPercentile(tStart, tEnd, percentile, false);
|
||||
}
|
||||
|
||||
double getPercentile(long tStart, long tEnd, double percentile, boolean includenan) throws RrdException {
|
||||
Aggregator agg = new Aggregator(timestamps, values);
|
||||
return agg.getPercentile(tStart, tEnd, percentile, includenan);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
JRobin data management.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,27 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.graph;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
class Area extends SourcedPlotElement {
|
||||
Area(String srcName, Paint color) {
|
||||
super(srcName, color);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.graph;
|
||||
|
||||
import org.jrobin.data.DataProcessor;
|
||||
|
||||
class CDef extends Source {
|
||||
private final String rpnExpression;
|
||||
|
||||
CDef(String name, String rpnExpression) {
|
||||
super(name);
|
||||
this.rpnExpression = rpnExpression;
|
||||
}
|
||||
|
||||
void requestData(DataProcessor dproc) {
|
||||
dproc.addDatasource(name, rpnExpression);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.graph;
|
||||
|
||||
import org.jrobin.core.RrdException;
|
||||
import org.jrobin.data.DataProcessor;
|
||||
|
||||
class CommentText implements RrdGraphConstants {
|
||||
private final String text; // original text
|
||||
|
||||
String resolvedText; // resolved text
|
||||
String marker; // end-of-text marker
|
||||
boolean enabled; // hrule and vrule comments can be disabled at runtime
|
||||
int x, y; // coordinates, evaluated later
|
||||
|
||||
CommentText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
void resolveText(DataProcessor dproc, ValueScaler valueScaler) throws RrdException {
|
||||
resolvedText = text;
|
||||
marker = "";
|
||||
if (resolvedText != null) {
|
||||
for (String mark : MARKERS) {
|
||||
if (resolvedText.endsWith(mark)) {
|
||||
marker = mark;
|
||||
resolvedText = resolvedText.substring(0, resolvedText.length() - marker.length());
|
||||
trimIfGlue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
enabled = resolvedText != null;
|
||||
}
|
||||
|
||||
void trimIfGlue() {
|
||||
if (marker.equals(GLUE_MARKER)) {
|
||||
resolvedText = resolvedText.replaceFirst("\\s+$", "");
|
||||
}
|
||||
}
|
||||
|
||||
boolean isPrint() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isValidGraphElement() {
|
||||
return !isPrint() && enabled;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.graph;
|
||||
|
||||
import org.jrobin.data.DataProcessor;
|
||||
|
||||
class Def extends Source {
|
||||
private final String rrdPath, dsName, consolFun, backend;
|
||||
|
||||
Def(String name, String rrdPath, String dsName, String consolFun) {
|
||||
this(name, rrdPath, dsName, consolFun, null);
|
||||
}
|
||||
|
||||
Def(String name, String rrdPath, String dsName, String consolFun, String backend) {
|
||||
super(name);
|
||||
this.rrdPath = rrdPath;
|
||||
this.dsName = dsName;
|
||||
this.consolFun = consolFun;
|
||||
this.backend = backend;
|
||||
}
|
||||
|
||||
void requestData(DataProcessor dproc) {
|
||||
if (backend == null) {
|
||||
dproc.addDatasource(name, rrdPath, dsName, consolFun);
|
||||
}
|
||||
else {
|
||||
dproc.addDatasource(name, rrdPath, dsName, consolFun, backend);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,728 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// GifEncoder from J.M.G. Elliott
|
||||
// http://jmge.net/java/gifenc/
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
package org.jrobin.graph;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.PixelGrabber;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Vector;
|
||||
|
||||
class GifEncoder {
|
||||
private Dimension dispDim = new Dimension(0, 0);
|
||||
private GifColorTable colorTable;
|
||||
private int bgIndex = 0;
|
||||
private int loopCount = 1;
|
||||
private String theComments;
|
||||
private Vector<Gif89Frame> vFrames = new Vector<Gif89Frame>();
|
||||
|
||||
GifEncoder() {
|
||||
colorTable = new GifColorTable();
|
||||
}
|
||||
|
||||
GifEncoder(Image static_image) throws IOException {
|
||||
this();
|
||||
addFrame(static_image);
|
||||
}
|
||||
|
||||
GifEncoder(Color[] colors) {
|
||||
colorTable = new GifColorTable(colors);
|
||||
}
|
||||
|
||||
GifEncoder(Color[] colors, int width, int height, byte ci_pixels[])
|
||||
throws IOException {
|
||||
this(colors);
|
||||
addFrame(width, height, ci_pixels);
|
||||
}
|
||||
|
||||
int getFrameCount() {
|
||||
return vFrames.size();
|
||||
}
|
||||
|
||||
Gif89Frame getFrameAt(int index) {
|
||||
return isOk(index) ? vFrames.elementAt(index) : null;
|
||||
}
|
||||
|
||||
void addFrame(Gif89Frame gf) throws IOException {
|
||||
accommodateFrame(gf);
|
||||
vFrames.addElement(gf);
|
||||
}
|
||||
|
||||
void addFrame(Image image) throws IOException {
|
||||
addFrame(new DirectGif89Frame(image));
|
||||
}
|
||||
|
||||
void addFrame(int width, int height, byte ci_pixels[])
|
||||
throws IOException {
|
||||
addFrame(new IndexGif89Frame(width, height, ci_pixels));
|
||||
}
|
||||
|
||||
void insertFrame(int index, Gif89Frame gf) throws IOException {
|
||||
accommodateFrame(gf);
|
||||
vFrames.insertElementAt(gf, index);
|
||||
}
|
||||
|
||||
void setTransparentIndex(int index) {
|
||||
colorTable.setTransparent(index);
|
||||
}
|
||||
|
||||
void setLogicalDisplay(Dimension dim, int background) {
|
||||
dispDim = new Dimension(dim);
|
||||
bgIndex = background;
|
||||
}
|
||||
|
||||
void setLoopCount(int count) {
|
||||
loopCount = count;
|
||||
}
|
||||
|
||||
void setComments(String comments) {
|
||||
theComments = comments;
|
||||
}
|
||||
|
||||
void setUniformDelay(int interval) {
|
||||
for (int i = 0; i < vFrames.size(); ++i) {
|
||||
vFrames.elementAt(i).setDelay(interval);
|
||||
}
|
||||
}
|
||||
|
||||
void encode(OutputStream out) throws IOException {
|
||||
int nframes = getFrameCount();
|
||||
boolean is_sequence = nframes > 1;
|
||||
colorTable.closePixelProcessing();
|
||||
Put.ascii("GIF89a", out);
|
||||
writeLogicalScreenDescriptor(out);
|
||||
colorTable.encode(out);
|
||||
if (is_sequence && loopCount != 1) {
|
||||
writeNetscapeExtension(out);
|
||||
}
|
||||
if (theComments != null && theComments.length() > 0) {
|
||||
writeCommentExtension(out);
|
||||
}
|
||||
for (int i = 0; i < nframes; ++i) {
|
||||
vFrames.elementAt(i).encode(
|
||||
out, is_sequence, colorTable.getDepth(), colorTable.getTransparent()
|
||||
);
|
||||
}
|
||||
out.write((int) ';');
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private void accommodateFrame(Gif89Frame gf) throws IOException {
|
||||
dispDim.width = Math.max(dispDim.width, gf.getWidth());
|
||||
dispDim.height = Math.max(dispDim.height, gf.getHeight());
|
||||
colorTable.processPixels(gf);
|
||||
}
|
||||
|
||||
private void writeLogicalScreenDescriptor(OutputStream os) throws IOException {
|
||||
Put.leShort(dispDim.width, os);
|
||||
Put.leShort(dispDim.height, os);
|
||||
os.write(0xf0 | colorTable.getDepth() - 1);
|
||||
os.write(bgIndex);
|
||||
os.write(0);
|
||||
}
|
||||
|
||||
|
||||
private void writeNetscapeExtension(OutputStream os) throws IOException {
|
||||
os.write((int) '!');
|
||||
os.write(0xff);
|
||||
os.write(11);
|
||||
Put.ascii("NETSCAPE2.0", os);
|
||||
os.write(3);
|
||||
os.write(1);
|
||||
Put.leShort(loopCount > 1 ? loopCount - 1 : 0, os);
|
||||
os.write(0);
|
||||
}
|
||||
|
||||
|
||||
private void writeCommentExtension(OutputStream os) throws IOException {
|
||||
os.write((int) '!');
|
||||
os.write(0xfe);
|
||||
int remainder = theComments.length() % 255;
|
||||
int nsubblocks_full = theComments.length() / 255;
|
||||
int nsubblocks = nsubblocks_full + (remainder > 0 ? 1 : 0);
|
||||
int ibyte = 0;
|
||||
for (int isb = 0; isb < nsubblocks; ++isb) {
|
||||
int size = isb < nsubblocks_full ? 255 : remainder;
|
||||
os.write(size);
|
||||
Put.ascii(theComments.substring(ibyte, ibyte + size), os);
|
||||
ibyte += size;
|
||||
}
|
||||
os.write(0);
|
||||
}
|
||||
|
||||
|
||||
private boolean isOk(int frame_index) {
|
||||
return frame_index >= 0 && frame_index < vFrames.size();
|
||||
}
|
||||
}
|
||||
|
||||
class DirectGif89Frame extends Gif89Frame {
|
||||
private int[] argbPixels;
|
||||
|
||||
DirectGif89Frame(Image img) throws IOException {
|
||||
PixelGrabber pg = new PixelGrabber(img, 0, 0, -1, -1, true);
|
||||
String errmsg = null;
|
||||
try {
|
||||
if (!pg.grabPixels()) {
|
||||
errmsg = "can't grab pixels from image";
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
errmsg = "interrupted grabbing pixels from image";
|
||||
}
|
||||
if (errmsg != null) {
|
||||
throw new IOException(errmsg + " (" + getClass().getName() + ")");
|
||||
}
|
||||
theWidth = pg.getWidth();
|
||||
theHeight = pg.getHeight();
|
||||
argbPixels = (int[]) pg.getPixels();
|
||||
ciPixels = new byte[argbPixels.length];
|
||||
}
|
||||
|
||||
DirectGif89Frame(int width, int height, int argb_pixels[]) {
|
||||
theWidth = width;
|
||||
theHeight = height;
|
||||
argbPixels = new int[theWidth * theHeight];
|
||||
System.arraycopy(argb_pixels, 0, argbPixels, 0, argbPixels.length);
|
||||
ciPixels = new byte[argbPixels.length];
|
||||
}
|
||||
|
||||
Object getPixelSource() {
|
||||
return argbPixels;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GifColorTable {
|
||||
private int[] theColors = new int[256];
|
||||
private int colorDepth;
|
||||
private int transparentIndex = -1;
|
||||
private int ciCount = 0;
|
||||
private ReverseColorMap ciLookup;
|
||||
|
||||
GifColorTable() {
|
||||
ciLookup = new ReverseColorMap();
|
||||
}
|
||||
|
||||
GifColorTable(Color[] colors) {
|
||||
int n2copy = Math.min(theColors.length, colors.length);
|
||||
for (int i = 0; i < n2copy; ++i) {
|
||||
theColors[i] = colors[i].getRGB();
|
||||
}
|
||||
}
|
||||
|
||||
int getDepth() {
|
||||
return colorDepth;
|
||||
}
|
||||
|
||||
int getTransparent() {
|
||||
return transparentIndex;
|
||||
}
|
||||
|
||||
void setTransparent(int color_index) {
|
||||
transparentIndex = color_index;
|
||||
}
|
||||
|
||||
void processPixels(Gif89Frame gf) throws IOException {
|
||||
if (gf instanceof DirectGif89Frame) {
|
||||
filterPixels((DirectGif89Frame) gf);
|
||||
}
|
||||
else {
|
||||
trackPixelUsage((IndexGif89Frame) gf);
|
||||
}
|
||||
}
|
||||
|
||||
void closePixelProcessing() {
|
||||
colorDepth = computeColorDepth(ciCount);
|
||||
}
|
||||
|
||||
void encode(OutputStream os) throws IOException {
|
||||
int palette_size = 1 << colorDepth;
|
||||
for (int i = 0; i < palette_size; ++i) {
|
||||
os.write(theColors[i] >> 16 & 0xff);
|
||||
os.write(theColors[i] >> 8 & 0xff);
|
||||
os.write(theColors[i] & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
private void filterPixels(DirectGif89Frame dgf) throws IOException {
|
||||
if (ciLookup == null) {
|
||||
throw new IOException("RGB frames require palette autodetection");
|
||||
}
|
||||
int[] argb_pixels = (int[]) dgf.getPixelSource();
|
||||
byte[] ci_pixels = dgf.getPixelSink();
|
||||
int npixels = argb_pixels.length;
|
||||
for (int i = 0; i < npixels; ++i) {
|
||||
int argb = argb_pixels[i];
|
||||
if ((argb >>> 24) < 0x80) {
|
||||
if (transparentIndex == -1) {
|
||||
transparentIndex = ciCount;
|
||||
}
|
||||
else if (argb != theColors[transparentIndex]) {
|
||||
ci_pixels[i] = (byte) transparentIndex;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int color_index = ciLookup.getPaletteIndex(argb & 0xffffff);
|
||||
if (color_index == -1) {
|
||||
if (ciCount == 256) {
|
||||
throw new IOException("can't encode as GIF (> 256 colors)");
|
||||
}
|
||||
theColors[ciCount] = argb;
|
||||
ciLookup.put(argb & 0xffffff, ciCount);
|
||||
ci_pixels[i] = (byte) ciCount;
|
||||
++ciCount;
|
||||
}
|
||||
else {
|
||||
ci_pixels[i] = (byte) color_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void trackPixelUsage(IndexGif89Frame igf) {
|
||||
byte[] ci_pixels = (byte[]) igf.getPixelSource();
|
||||
int npixels = ci_pixels.length;
|
||||
for (int i = 0; i < npixels; ++i) {
|
||||
if (ci_pixels[i] >= ciCount) {
|
||||
ciCount = ci_pixels[i] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int computeColorDepth(int colorcount) {
|
||||
if (colorcount <= 2) {
|
||||
return 1;
|
||||
}
|
||||
if (colorcount <= 4) {
|
||||
return 2;
|
||||
}
|
||||
if (colorcount <= 16) {
|
||||
return 4;
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
class ReverseColorMap {
|
||||
private static class ColorRecord {
|
||||
int rgb;
|
||||
int ipalette;
|
||||
|
||||
ColorRecord(int rgb, int ipalette) {
|
||||
this.rgb = rgb;
|
||||
this.ipalette = ipalette;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int HCAPACITY = 2053;
|
||||
private ColorRecord[] hTable = new ColorRecord[HCAPACITY];
|
||||
|
||||
int getPaletteIndex(int rgb) {
|
||||
ColorRecord rec;
|
||||
for (int itable = rgb % hTable.length;
|
||||
(rec = hTable[itable]) != null && rec.rgb != rgb;
|
||||
itable = ++itable % hTable.length
|
||||
) {
|
||||
;
|
||||
}
|
||||
if (rec != null) {
|
||||
return rec.ipalette;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void put(int rgb, int ipalette) {
|
||||
int itable;
|
||||
for (itable = rgb % hTable.length;
|
||||
hTable[itable] != null;
|
||||
itable = ++itable % hTable.length
|
||||
) {
|
||||
;
|
||||
}
|
||||
hTable[itable] = new ColorRecord(rgb, ipalette);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Gif89Frame {
|
||||
static final int DM_UNDEFINED = 0;
|
||||
static final int DM_LEAVE = 1;
|
||||
static final int DM_BGCOLOR = 2;
|
||||
static final int DM_REVERT = 3;
|
||||
int theWidth = -1;
|
||||
int theHeight = -1;
|
||||
byte[] ciPixels;
|
||||
|
||||
private Point thePosition = new Point(0, 0);
|
||||
private boolean isInterlaced;
|
||||
private int csecsDelay;
|
||||
private int disposalCode = DM_LEAVE;
|
||||
|
||||
void setPosition(Point p) {
|
||||
thePosition = new Point(p);
|
||||
}
|
||||
|
||||
void setInterlaced(boolean b) {
|
||||
isInterlaced = b;
|
||||
}
|
||||
|
||||
void setDelay(int interval) {
|
||||
csecsDelay = interval;
|
||||
}
|
||||
|
||||
void setDisposalMode(int code) {
|
||||
disposalCode = code;
|
||||
}
|
||||
|
||||
Gif89Frame() {
|
||||
}
|
||||
|
||||
abstract Object getPixelSource();
|
||||
|
||||
int getWidth() {
|
||||
return theWidth;
|
||||
}
|
||||
|
||||
int getHeight() {
|
||||
return theHeight;
|
||||
}
|
||||
|
||||
byte[] getPixelSink() {
|
||||
return ciPixels;
|
||||
}
|
||||
|
||||
void encode(OutputStream os, boolean epluribus, int color_depth,
|
||||
int transparent_index) throws IOException {
|
||||
writeGraphicControlExtension(os, epluribus, transparent_index);
|
||||
writeImageDescriptor(os);
|
||||
new GifPixelsEncoder(
|
||||
theWidth, theHeight, ciPixels, isInterlaced, color_depth
|
||||
).encode(os);
|
||||
}
|
||||
|
||||
private void writeGraphicControlExtension(OutputStream os, boolean epluribus,
|
||||
int itransparent) throws IOException {
|
||||
int transflag = itransparent == -1 ? 0 : 1;
|
||||
if (transflag == 1 || epluribus) {
|
||||
os.write((int) '!');
|
||||
os.write(0xf9);
|
||||
os.write(4);
|
||||
os.write((disposalCode << 2) | transflag);
|
||||
Put.leShort(csecsDelay, os);
|
||||
os.write(itransparent);
|
||||
os.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeImageDescriptor(OutputStream os) throws IOException {
|
||||
os.write((int) ',');
|
||||
Put.leShort(thePosition.x, os);
|
||||
Put.leShort(thePosition.y, os);
|
||||
Put.leShort(theWidth, os);
|
||||
Put.leShort(theHeight, os);
|
||||
os.write(isInterlaced ? 0x40 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
class GifPixelsEncoder {
|
||||
private static final int EOF = -1;
|
||||
private int imgW, imgH;
|
||||
private byte[] pixAry;
|
||||
private boolean wantInterlaced;
|
||||
private int initCodeSize;
|
||||
private int countDown;
|
||||
private int xCur, yCur;
|
||||
private int curPass;
|
||||
|
||||
GifPixelsEncoder(int width, int height, byte[] pixels, boolean interlaced,
|
||||
int color_depth) {
|
||||
imgW = width;
|
||||
imgH = height;
|
||||
pixAry = pixels;
|
||||
wantInterlaced = interlaced;
|
||||
initCodeSize = Math.max(2, color_depth);
|
||||
}
|
||||
|
||||
void encode(OutputStream os) throws IOException {
|
||||
os.write(initCodeSize);
|
||||
|
||||
countDown = imgW * imgH;
|
||||
xCur = yCur = curPass = 0;
|
||||
|
||||
compress(initCodeSize + 1, os);
|
||||
|
||||
os.write(0);
|
||||
}
|
||||
|
||||
private void bumpPosition() {
|
||||
++xCur;
|
||||
if (xCur == imgW) {
|
||||
xCur = 0;
|
||||
if (!wantInterlaced) {
|
||||
++yCur;
|
||||
}
|
||||
else {
|
||||
switch (curPass) {
|
||||
case 0:
|
||||
yCur += 8;
|
||||
if (yCur >= imgH) {
|
||||
++curPass;
|
||||
yCur = 4;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
yCur += 8;
|
||||
if (yCur >= imgH) {
|
||||
++curPass;
|
||||
yCur = 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
yCur += 4;
|
||||
if (yCur >= imgH) {
|
||||
++curPass;
|
||||
yCur = 1;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
yCur += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int nextPixel() {
|
||||
if (countDown == 0) {
|
||||
return EOF;
|
||||
}
|
||||
--countDown;
|
||||
byte pix = pixAry[yCur * imgW + xCur];
|
||||
bumpPosition();
|
||||
return pix & 0xff;
|
||||
}
|
||||
|
||||
static final int BITS = 12;
|
||||
static final int HSIZE = 5003;
|
||||
int n_bits;
|
||||
int maxbits = BITS;
|
||||
int maxcode;
|
||||
int maxmaxcode = 1 << BITS;
|
||||
|
||||
final int MAXCODE(int n_bits) {
|
||||
return (1 << n_bits) - 1;
|
||||
}
|
||||
|
||||
int[] htab = new int[HSIZE];
|
||||
int[] codetab = new int[HSIZE];
|
||||
int hsize = HSIZE;
|
||||
int free_ent = 0;
|
||||
boolean clear_flg = false;
|
||||
int g_init_bits;
|
||||
int ClearCode;
|
||||
int EOFCode;
|
||||
|
||||
void compress(int init_bits, OutputStream outs) throws IOException {
|
||||
int fcode;
|
||||
int i /* = 0 */;
|
||||
int c;
|
||||
int ent;
|
||||
int disp;
|
||||
int hsize_reg;
|
||||
int hshift;
|
||||
g_init_bits = init_bits;
|
||||
clear_flg = false;
|
||||
n_bits = g_init_bits;
|
||||
maxcode = MAXCODE(n_bits);
|
||||
ClearCode = 1 << (init_bits - 1);
|
||||
EOFCode = ClearCode + 1;
|
||||
free_ent = ClearCode + 2;
|
||||
|
||||
char_init();
|
||||
ent = nextPixel();
|
||||
hshift = 0;
|
||||
for (fcode = hsize; fcode < 65536; fcode *= 2) {
|
||||
++hshift;
|
||||
}
|
||||
hshift = 8 - hshift;
|
||||
hsize_reg = hsize;
|
||||
cl_hash(hsize_reg);
|
||||
output(ClearCode, outs);
|
||||
outer_loop:
|
||||
while ((c = nextPixel()) != EOF) {
|
||||
fcode = (c << maxbits) + ent;
|
||||
i = (c << hshift) ^ ent;
|
||||
if (htab[i] == fcode) {
|
||||
ent = codetab[i];
|
||||
continue;
|
||||
}
|
||||
else if (htab[i] >= 0) {
|
||||
disp = hsize_reg - i;
|
||||
if (i == 0) {
|
||||
disp = 1;
|
||||
}
|
||||
do {
|
||||
if ((i -= disp) < 0) {
|
||||
i += hsize_reg;
|
||||
}
|
||||
|
||||
if (htab[i] == fcode) {
|
||||
ent = codetab[i];
|
||||
continue outer_loop;
|
||||
}
|
||||
} while (htab[i] >= 0);
|
||||
}
|
||||
output(ent, outs);
|
||||
ent = c;
|
||||
if (free_ent < maxmaxcode) {
|
||||
codetab[i] = free_ent++;
|
||||
htab[i] = fcode;
|
||||
}
|
||||
else {
|
||||
cl_block(outs);
|
||||
}
|
||||
}
|
||||
output(ent, outs);
|
||||
output(EOFCode, outs);
|
||||
}
|
||||
|
||||
int cur_accum = 0;
|
||||
int cur_bits = 0;
|
||||
int masks[] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
|
||||
0x001F, 0x003F, 0x007F, 0x00FF,
|
||||
0x01FF, 0x03FF, 0x07FF, 0x0FFF,
|
||||
0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF};
|
||||
|
||||
void output(int code, OutputStream outs) throws IOException {
|
||||
cur_accum &= masks[cur_bits];
|
||||
if (cur_bits > 0) {
|
||||
cur_accum |= (code << cur_bits);
|
||||
}
|
||||
else {
|
||||
cur_accum = code;
|
||||
}
|
||||
|
||||
cur_bits += n_bits;
|
||||
|
||||
while (cur_bits >= 8) {
|
||||
char_out((byte) (cur_accum & 0xff), outs);
|
||||
cur_accum >>= 8;
|
||||
cur_bits -= 8;
|
||||
}
|
||||
if (free_ent > maxcode || clear_flg) {
|
||||
if (clear_flg) {
|
||||
maxcode = MAXCODE(n_bits = g_init_bits);
|
||||
clear_flg = false;
|
||||
}
|
||||
else {
|
||||
++n_bits;
|
||||
if (n_bits == maxbits) {
|
||||
maxcode = maxmaxcode;
|
||||
}
|
||||
else {
|
||||
maxcode = MAXCODE(n_bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (code == EOFCode) {
|
||||
|
||||
while (cur_bits > 0) {
|
||||
char_out((byte) (cur_accum & 0xff), outs);
|
||||
cur_accum >>= 8;
|
||||
cur_bits -= 8;
|
||||
}
|
||||
flush_char(outs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cl_block(OutputStream outs) throws IOException {
|
||||
cl_hash(hsize);
|
||||
free_ent = ClearCode + 2;
|
||||
clear_flg = true;
|
||||
|
||||
output(ClearCode, outs);
|
||||
}
|
||||
|
||||
|
||||
void cl_hash(int hsize) {
|
||||
for (int i = 0; i < hsize; ++i) {
|
||||
htab[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int a_count;
|
||||
|
||||
void char_init() {
|
||||
a_count = 0;
|
||||
}
|
||||
|
||||
byte[] accum = new byte[256];
|
||||
|
||||
void char_out(byte c, OutputStream outs) throws IOException {
|
||||
accum[a_count++] = c;
|
||||
if (a_count >= 254) {
|
||||
flush_char(outs);
|
||||
}
|
||||
}
|
||||
|
||||
void flush_char(OutputStream outs) throws IOException {
|
||||
if (a_count > 0) {
|
||||
outs.write(a_count);
|
||||
outs.write(accum, 0, a_count);
|
||||
a_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IndexGif89Frame extends Gif89Frame {
|
||||
|
||||
IndexGif89Frame(int width, int height, byte ci_pixels[]) {
|
||||
theWidth = width;
|
||||
theHeight = height;
|
||||
ciPixels = new byte[theWidth * theHeight];
|
||||
System.arraycopy(ci_pixels, 0, ciPixels, 0, ciPixels.length);
|
||||
}
|
||||
|
||||
Object getPixelSource() {
|
||||
return ciPixels;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class Put {
|
||||
static void ascii(String s, OutputStream os) throws IOException {
|
||||
byte[] bytes = new byte[s.length()];
|
||||
for (int i = 0; i < bytes.length; ++i) {
|
||||
bytes[i] = (byte) s.charAt(i);
|
||||
}
|
||||
os.write(bytes);
|
||||
}
|
||||
|
||||
static void leShort(int i16, OutputStream os) throws IOException {
|
||||
os.write(i16 & 0xff);
|
||||
os.write(i16 >> 8 & 0xff);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.graph;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
class HRule extends Rule {
|
||||
final double value;
|
||||
|
||||
HRule(double value, Paint color, LegendText legend, float width) {
|
||||
super(color, legend, width);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
void setLegendVisibility(double minval, double maxval, boolean forceLegend) {
|
||||
legend.enabled &= (forceLegend || (value >= minval && value <= maxval));
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.graph;
|
||||
|
||||
class ImageParameters {
|
||||
long start, end;
|
||||
double minval, maxval;
|
||||
int unitsexponent;
|
||||
double base;
|
||||
double magfact;
|
||||
char symbol;
|
||||
double ygridstep;
|
||||
int ylabfact;
|
||||
double decimals;
|
||||
int quadrant;
|
||||
double scaledstep;
|
||||
int xsize;
|
||||
int ysize;
|
||||
int xorigin;
|
||||
int yorigin;
|
||||
int unitslength;
|
||||
int xgif, ygif;
|
||||
String unit;
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.graph;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.font.LineMetrics;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.util.Iterator;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
|
||||
class ImageWorker {
|
||||
private static final String DUMMY_TEXT = "Dummy";
|
||||
|
||||
private BufferedImage img;
|
||||
private Graphics2D gd;
|
||||
private int imgWidth, imgHeight;
|
||||
private AffineTransform aftInitial;
|
||||
|
||||
ImageWorker(int width, int height) {
|
||||
resize(width, height);
|
||||
}
|
||||
|
||||
void resize(int width, int height) {
|
||||
if (gd != null) {
|
||||
gd.dispose();
|
||||
}
|
||||
this.imgWidth = width;
|
||||
this.imgHeight = height;
|
||||
this.img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
this.gd = img.createGraphics();
|
||||
this.aftInitial = gd.getTransform();
|
||||
this.setAntiAliasing(false);
|
||||
}
|
||||
|
||||
void clip(int x, int y, int width, int height) {
|
||||
gd.setClip(x, y, width, height);
|
||||
}
|
||||
|
||||
void transform(int x, int y, double angle) {
|
||||
gd.translate(x, y);
|
||||
gd.rotate(angle);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
gd.setTransform(aftInitial);
|
||||
gd.setClip(0, 0, imgWidth, imgHeight);
|
||||
}
|
||||
|
||||
void fillRect(int x, int y, int width, int height, Paint paint) {
|
||||
gd.setPaint(paint);
|
||||
gd.fillRect(x, y, width, height);
|
||||
}
|
||||
|
||||
void fillPolygon(int[] x, int[] y, Paint paint) {
|
||||
gd.setPaint(paint);
|
||||
gd.fillPolygon(x, y, x.length);
|
||||
}
|
||||
|
||||
void fillPolygon(double[] x, double yBottom, double[] yTop, Paint paint) {
|
||||
gd.setPaint(paint);
|
||||
PathIterator path = new PathIterator(yTop);
|
||||
for (int[] pos = path.getNextPath(); pos != null; pos = path.getNextPath()) {
|
||||
int start = pos[0], end = pos[1], n = end - start;
|
||||
int[] xDev = new int[n + 2], yDev = new int[n + 2];
|
||||
for (int i = start; i < end; i++) {
|
||||
xDev[i - start] = (int) x[i];
|
||||
yDev[i - start] = (int) yTop[i];
|
||||
}
|
||||
xDev[n] = xDev[n - 1];
|
||||
xDev[n + 1] = xDev[0];
|
||||
yDev[n] = yDev[n + 1] = (int) yBottom;
|
||||
gd.fillPolygon(xDev, yDev, xDev.length);
|
||||
gd.drawPolygon(xDev, yDev, xDev.length);
|
||||
}
|
||||
}
|
||||
|
||||
void fillPolygon(double[] x, double[] yBottom, double[] yTop, Paint paint) {
|
||||
gd.setPaint(paint);
|
||||
PathIterator path = new PathIterator(yTop);
|
||||
for (int[] pos = path.getNextPath(); pos != null; pos = path.getNextPath()) {
|
||||
int start = pos[0], end = pos[1], n = end - start;
|
||||
int[] xDev = new int[n * 2], yDev = new int[n * 2];
|
||||
for (int i = start; i < end; i++) {
|
||||
int ix1 = i - start, ix2 = n * 2 - 1 - i + start;
|
||||
xDev[ix1] = xDev[ix2] = (int) x[i];
|
||||
yDev[ix1] = (int) yTop[i];
|
||||
yDev[ix2] = (int) yBottom[i];
|
||||
}
|
||||
gd.fillPolygon(xDev, yDev, xDev.length);
|
||||
gd.drawPolygon(xDev, yDev, xDev.length);
|
||||
}
|
||||
}
|
||||
|
||||
void drawLine(int x1, int y1, int x2, int y2, Paint paint, Stroke stroke) {
|
||||
gd.setStroke(stroke);
|
||||
gd.setPaint(paint);
|
||||
gd.drawLine(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
void drawPolyline(int[] x, int[] y, Paint paint, Stroke stroke) {
|
||||
gd.setStroke(stroke);
|
||||
gd.setPaint(paint);
|
||||
gd.drawPolyline(x, y, x.length);
|
||||
}
|
||||
|
||||
void drawPolyline(double[] x, double[] y, Paint paint, Stroke stroke) {
|
||||
gd.setPaint(paint);
|
||||
gd.setStroke(stroke);
|
||||
PathIterator path = new PathIterator(y);
|
||||
for (int[] pos = path.getNextPath(); pos != null; pos = path.getNextPath()) {
|
||||
int start = pos[0], end = pos[1];
|
||||
int[] xDev = new int[end - start], yDev = new int[end - start];
|
||||
for (int i = start; i < end; i++) {
|
||||
xDev[i - start] = (int) x[i];
|
||||
yDev[i - start] = (int) y[i];
|
||||
}
|
||||
gd.drawPolyline(xDev, yDev, xDev.length);
|
||||
}
|
||||
}
|
||||
|
||||
void drawString(String text, int x, int y, Font font, Paint paint) {
|
||||
gd.setFont(font);
|
||||
gd.setPaint(paint);
|
||||
gd.drawString(text, x, y);
|
||||
}
|
||||
|
||||
double getFontAscent(Font font) {
|
||||
LineMetrics lm = font.getLineMetrics(DUMMY_TEXT, gd.getFontRenderContext());
|
||||
return lm.getAscent();
|
||||
}
|
||||
|
||||
double getFontHeight(Font font) {
|
||||
LineMetrics lm = font.getLineMetrics(DUMMY_TEXT, gd.getFontRenderContext());
|
||||
return lm.getAscent() + lm.getDescent();
|
||||
}
|
||||
|
||||
double getStringWidth(String text, Font font) {
|
||||
return font.getStringBounds(text, 0, text.length(), gd.getFontRenderContext()).getBounds().getWidth();
|
||||
}
|
||||
|
||||
void setAntiAliasing(boolean enable) {
|
||||
gd.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
enable ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
|
||||
gd.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
gd.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
gd.dispose();
|
||||
}
|
||||
|
||||
void saveImage(OutputStream stream, String type, float quality) throws IOException {
|
||||
if (type.equalsIgnoreCase("png")) {
|
||||
ImageIO.write(img, "png", stream);
|
||||
}
|
||||
else if (type.equalsIgnoreCase("gif")) {
|
||||
GifEncoder gifEncoder = new GifEncoder(img);
|
||||
gifEncoder.encode(stream);
|
||||
}
|
||||
else if (type.equalsIgnoreCase("jpg") || type.equalsIgnoreCase("jpeg")) {
|
||||
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpg");
|
||||
ImageWriter writer = iter.next();
|
||||
ImageWriteParam iwp = writer.getDefaultWriteParam();
|
||||
|
||||
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
iwp.setCompressionQuality(quality);
|
||||
writer.setOutput(stream);
|
||||
writer.write(img);
|
||||
writer.dispose();
|
||||
}
|
||||
else {
|
||||
throw new IOException("Unsupported image format: " + type);
|
||||
}
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
byte[] saveImage(String path, String type, float quality) throws IOException {
|
||||
byte[] bytes = getImageBytes(type, quality);
|
||||
RandomAccessFile f = new RandomAccessFile(path, "rw");
|
||||
try {
|
||||
f.write(bytes);
|
||||
return bytes;
|
||||
} finally {
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
byte[] getImageBytes(String type, float quality) throws IOException {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
try {
|
||||
saveImage(stream, type, quality);
|
||||
return stream.toByteArray();
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadImage(String imageFile) throws IOException {
|
||||
BufferedImage wpImage = ImageIO.read(new File(imageFile));
|
||||
TexturePaint paint = new TexturePaint(wpImage, new Rectangle(0, 0, wpImage.getWidth(), wpImage.getHeight()));
|
||||
gd.setPaint(paint);
|
||||
gd.fillRect(0, 0, wpImage.getWidth(), wpImage.getHeight());
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class LegendComposer implements RrdGraphConstants {
|
||||
private RrdGraphDef gdef;
|
||||
private ImageWorker worker;
|
||||
private int legX, legY, legWidth;
|
||||
private double interlegendSpace;
|
||||
private double leading;
|
||||
private double smallLeading;
|
||||
private double boxSpace;
|
||||
|
||||
LegendComposer(RrdGraph rrdGraph, int legX, int legY, int legWidth) {
|
||||
this.gdef = rrdGraph.gdef;
|
||||
this.worker = rrdGraph.worker;
|
||||
this.legX = legX;
|
||||
this.legY = legY;
|
||||
this.legWidth = legWidth;
|
||||
interlegendSpace = rrdGraph.getInterlegendSpace();
|
||||
leading = rrdGraph.getLeading();
|
||||
smallLeading = rrdGraph.getSmallLeading();
|
||||
boxSpace = rrdGraph.getBoxSpace();
|
||||
}
|
||||
|
||||
int placeComments() {
|
||||
Line line = new Line();
|
||||
for (CommentText comment : gdef.comments) {
|
||||
if (comment.isValidGraphElement()) {
|
||||
if (!line.canAccomodate(comment)) {
|
||||
line.layoutAndAdvance(false);
|
||||
line.clear();
|
||||
}
|
||||
line.add(comment);
|
||||
}
|
||||
}
|
||||
line.layoutAndAdvance(true);
|
||||
worker.dispose();
|
||||
return legY;
|
||||
}
|
||||
|
||||
class Line {
|
||||
private String lastMarker;
|
||||
private double width;
|
||||
private int spaceCount;
|
||||
private boolean noJustification;
|
||||
private List<CommentText> comments = new ArrayList<CommentText>();
|
||||
|
||||
Line() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
lastMarker = "";
|
||||
width = 0;
|
||||
spaceCount = 0;
|
||||
noJustification = false;
|
||||
comments.clear();
|
||||
}
|
||||
|
||||
boolean canAccomodate(CommentText comment) {
|
||||
// always accommodate if empty
|
||||
if (comments.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
// cannot accommodate if the last marker was \j, \l, \r, \c, \s
|
||||
if (lastMarker.equals(ALIGN_LEFT_MARKER) || lastMarker.equals(ALIGN_CENTER_MARKER) ||
|
||||
lastMarker.equals(ALIGN_RIGHT_MARKER) || lastMarker.equals(ALIGN_JUSTIFIED_MARKER) ||
|
||||
lastMarker.equals(VERTICAL_SPACING_MARKER)) {
|
||||
return false;
|
||||
}
|
||||
// cannot accommodate if line would be too long
|
||||
double commentWidth = getCommentWidth(comment);
|
||||
if (!lastMarker.equals(GLUE_MARKER)) {
|
||||
commentWidth += interlegendSpace;
|
||||
}
|
||||
return width + commentWidth <= legWidth;
|
||||
}
|
||||
|
||||
void add(CommentText comment) {
|
||||
double commentWidth = getCommentWidth(comment);
|
||||
if (comments.size() > 0 && !lastMarker.equals(GLUE_MARKER)) {
|
||||
commentWidth += interlegendSpace;
|
||||
spaceCount++;
|
||||
}
|
||||
width += commentWidth;
|
||||
lastMarker = comment.marker;
|
||||
noJustification |= lastMarker.equals(NO_JUSTIFICATION_MARKER);
|
||||
comments.add(comment);
|
||||
}
|
||||
|
||||
void layoutAndAdvance(boolean isLastLine) {
|
||||
if (comments.size() > 0) {
|
||||
if (lastMarker.equals(ALIGN_LEFT_MARKER)) {
|
||||
placeComments(legX, interlegendSpace);
|
||||
}
|
||||
else if (lastMarker.equals(ALIGN_RIGHT_MARKER)) {
|
||||
placeComments(legX + legWidth - width, interlegendSpace);
|
||||
}
|
||||
else if (lastMarker.equals(ALIGN_CENTER_MARKER)) {
|
||||
placeComments(legX + (legWidth - width) / 2.0, interlegendSpace);
|
||||
}
|
||||
else if (lastMarker.equals(ALIGN_JUSTIFIED_MARKER)) {
|
||||
// anything to justify?
|
||||
if (spaceCount > 0) {
|
||||
placeComments(legX, (legWidth - width) / spaceCount + interlegendSpace);
|
||||
}
|
||||
else {
|
||||
placeComments(legX, interlegendSpace);
|
||||
}
|
||||
}
|
||||
else if (lastMarker.equals(VERTICAL_SPACING_MARKER)) {
|
||||
placeComments(legX, interlegendSpace);
|
||||
}
|
||||
else {
|
||||
// nothing specified, align with respect to '\J'
|
||||
if (noJustification || isLastLine) {
|
||||
placeComments(legX, interlegendSpace);
|
||||
}
|
||||
else {
|
||||
placeComments(legX, (legWidth - width) / spaceCount + interlegendSpace);
|
||||
}
|
||||
}
|
||||
if (lastMarker.equals(VERTICAL_SPACING_MARKER)) {
|
||||
legY += smallLeading;
|
||||
}
|
||||
else {
|
||||
legY += leading;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double getCommentWidth(CommentText comment) {
|
||||
double commentWidth = worker.getStringWidth(comment.resolvedText, gdef.getFont(FONTTAG_LEGEND));
|
||||
if (comment instanceof LegendText) {
|
||||
commentWidth += boxSpace;
|
||||
}
|
||||
return commentWidth;
|
||||
}
|
||||
|
||||
private void placeComments(double xStart, double space) {
|
||||
double x = xStart;
|
||||
for (CommentText comment : comments) {
|
||||
comment.x = (int) x;
|
||||
comment.y = legY;
|
||||
x += getCommentWidth(comment);
|
||||
if (!comment.marker.equals(GLUE_MARKER)) {
|
||||
x += space;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.graph;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
class LegendText extends CommentText {
|
||||
final Paint legendColor;
|
||||
|
||||
LegendText(Paint legendColor, String text) {
|
||||
super(text);
|
||||
this.legendColor = legendColor;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.graph;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
class Line extends SourcedPlotElement {
|
||||
final float width;
|
||||
|
||||
Line(String srcName, Paint color, float width) {
|
||||
super(srcName, color);
|
||||
this.width = width;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
package org.jrobin.graph;
|
||||
|
||||
class Mapper {
|
||||
private RrdGraphDef gdef;
|
||||
private ImageParameters im;
|
||||
private double pixieX, pixieY;
|
||||
|
||||
Mapper(RrdGraph rrdGraph) {
|
||||
this(rrdGraph.gdef, rrdGraph.im);
|
||||
}
|
||||
|
||||
Mapper(RrdGraphDef gdef, ImageParameters im) {
|
||||
this.gdef = gdef;
|
||||
this.im = im;
|
||||
pixieX = (double) im.xsize / (double) (im.end - im.start);
|
||||
if (!gdef.logarithmic) {
|
||||
pixieY = (double) im.ysize / (im.maxval - im.minval);
|
||||
}
|
||||
else {
|
||||
pixieY = (double) im.ysize / (Math.log10(im.maxval) - Math.log10(im.minval));
|
||||
}
|
||||
}
|
||||
|
||||
int xtr(double mytime) {
|
||||
return (int) ((double) im.xorigin + pixieX * (mytime - im.start));
|
||||
}
|
||||
|
||||
int ytr(double value) {
|
||||
double yval;
|
||||
if (!gdef.logarithmic) {
|
||||
yval = im.yorigin - pixieY * (value - im.minval) + 0.5;
|
||||
}
|
||||
else {
|
||||
if (value < im.minval) {
|
||||
yval = im.yorigin;
|
||||
}
|
||||
else {
|
||||
yval = im.yorigin - pixieY * (Math.log10(value) - Math.log10(im.minval)) + 0.5;
|
||||
}
|
||||
}
|
||||
if (!gdef.rigid) {
|
||||
return (int) yval;
|
||||
}
|
||||
else if ((int) yval > im.yorigin) {
|
||||
return im.yorigin + 2;
|
||||
}
|
||||
else if ((int) yval < im.yorigin - im.ysize) {
|
||||
return im.yorigin - im.ysize - 2;
|
||||
}
|
||||
else {
|
||||
return (int) yval;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
|
||||
* Copyright (c) 2011 The OpenNMS Group, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*******************************************************************************/
|
||||
|
||||
package org.jrobin.graph;
|
||||
|
||||
import org.jrobin.core.Util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class Normalizer {
|
||||
final private double[] timestamps;
|
||||
final int count;
|
||||
final double step;
|
||||
|
||||
Normalizer(long tStart, long tEnd, int count) {
|
||||
this.count = count;
|
||||
this.step = (tEnd - tStart) / Double.valueOf(count - 1);
|
||||
this.timestamps = new double[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
this.timestamps[i] = tStart + ((double) i / (double) (count - 1)) * (tEnd - tStart);
|
||||
}
|
||||
}
|
||||
|
||||
double[] getTimestamps() {
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
double[] normalize(long[] rawTimestamps, double[] rawValues) {
|
||||
int rawCount = rawTimestamps.length;
|
||||
long rawStep = rawTimestamps[1] - rawTimestamps[0];
|
||||
// check if we have a simple match
|
||||
if (rawCount == count && rawStep == step && rawTimestamps[0] == timestamps[0]) {
|
||||
return getCopyOf(rawValues);
|
||||
}
|
||||
// reset all normalized values to NaN
|
||||
double[] values = new double[count];
|
||||
Arrays.fill(values, Double.NaN);
|
||||
for (int rawSeg = 0, seg = 0; rawSeg < rawCount && seg < count; rawSeg++) {
|
||||
double rawValue = rawValues[rawSeg];
|
||||
if (!Double.isNaN(rawValue)) {
|
||||
long rawLeft = rawTimestamps[rawSeg] - rawStep;
|
||||
while (seg < count && rawLeft >= timestamps[seg]) {
|
||||
seg++;
|
||||
}
|
||||
boolean overlap = true;
|
||||
for (int fillSeg = seg; overlap && fillSeg < count; fillSeg++) {
|
||||
double left = timestamps[fillSeg] - step;
|
||||
double t1 = Math.max(rawLeft, left);
|
||||
double t2 = Math.min(rawTimestamps[rawSeg], timestamps[fillSeg]);
|
||||
if (t1 < t2) {
|
||||
values[fillSeg] = Util.sum(values[fillSeg], (t2 - t1) * rawValues[rawSeg]);
|
||||
}
|
||||
else {
|
||||
overlap = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int seg = 0; seg < count; seg++) {
|
||||
values[seg] /= step;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private static double[] getCopyOf(double[] rawValues) {
|
||||
int n = rawValues.length;
|
||||
double[] values = new double[n];
|
||||
System.arraycopy(rawValues, 0, values, 0, n);
|
||||
return values;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user