Replace old jrobin with rrd4j 3.5 (ticket #2684)

2019-02-10
From https://github.com/rrd4j/rrd4j/releases
Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
Copyright (c) 2011 The OpenNMS Group, Inc.
Copyright 2011 The RRD4J Authors.
Apache 2.0 and LGPLv2.1

Unmodified as a base for future merges, except for omitted
files listed below.
This rev will not work; font patch to follow in next revision.

Following directories and files omitted:
converter/
core/RrdBerkeleyDbBackendFactory.java
core/RrdBerkeleyDbBackend.java
core/RrdMongoDBBackendFactory.java
core/RrdMongoDBBackend.java
demo/
graph/RrdGraphDefTemplate.java
inspector/
eu/bengreen/data/utility/
This commit is contained in:
zzz
2020-02-25 14:04:21 +00:00
parent 4826bbd333
commit 5bed4a0d7f
188 changed files with 8582 additions and 24540 deletions

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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 + "]";
}
}

View File

@ -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 + "]";
}
}

View File

@ -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 &gt;= 0 and &lt; 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 + "]";
}
}

View File

@ -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";
}

View File

@ -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
}
}

View File

@ -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 + "]";
}
}

View File

@ -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() + "]";
}
}

View File

@ -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";
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
// }
//}
//

View File

@ -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();
}
}

View File

@ -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>
* &lt;rrd_def&gt;
* &lt;path&gt;test.rrd&lt;/path&gt;
* &lt;!-- not mandatory --&gt;
* &lt;start&gt;1000123456&lt;/start&gt;
* &lt;!-- not mandatory --&gt;
* &lt;step&gt;300&lt;/step&gt;
* &lt;!-- at least one datasource must be supplied --&gt;
* &lt;datasource&gt;
* &lt;name&gt;input&lt;/name&gt;
* &lt;type&gt;COUNTER&lt;/type&gt;
* &lt;heartbeat&gt;300&lt;/heartbeat&gt;
* &lt;min&gt;0&lt;/min&gt;
* &lt;max&gt;U&lt;/max&gt;
* &lt;/datasource&gt;
* &lt;datasource&gt;
* &lt;name&gt;temperature&lt;/name&gt;
* &lt;type&gt;GAUGE&lt;/type&gt;
* &lt;heartbeat&gt;400&lt;/heartbeat&gt;
* &lt;min&gt;U&lt;/min&gt;
* &lt;max&gt;1000&lt;/max&gt;
* &lt;/datasource&gt;
* &lt;!-- at least one archive must be supplied --&gt;
* &lt;archive&gt;
* &lt;cf&gt;AVERAGE&lt;/cf&gt;
* &lt;xff&gt;0.5&lt;/xff&gt;
* &lt;steps&gt;1&lt;/steps&gt;
* &lt;rows&gt;600&lt;/rows&gt;
* &lt;/archive&gt;
* &lt;archive&gt;
* &lt;cf&gt;MAX&lt;/cf&gt;
* &lt;xff&gt;0.6&lt;/xff&gt;
* &lt;steps&gt;6&lt;/steps&gt;
* &lt;rows&gt;7000&lt;/rows&gt;
* &lt;/archive&gt;
* &lt;/rrd_def&gt;
* </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>&lt;some_tag&gt;</code> and
* <code>&lt;/some_tag&gt;</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>
* &lt;rrd_def&gt;
* &lt;path&gt;${path}&lt;/path&gt;
* &lt;step&gt;300&lt;/step&gt;
* ...
* </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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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 + "]";
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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 &quot;step&quot;. 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 &lt;root&gt;/classes subdirectory and that all jars (libraries) are placed in the
* &lt;root&gt;/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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 &lt;tag&gt;value&lt;/tag&gt; to output stream
*
* @param tag XML tag name
* @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</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 &lt;tag&gt;value&lt;/tag&gt; to output stream
*
* @param tag XML tag name
* @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
*/
public void writeTag(String tag, int value) {
writeTag(tag, "" + value);
}
/**
* Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
*
* @param tag XML tag name
* @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
*/
public void writeTag(String tag, long value) {
writeTag(tag, "" + value);
}
/**
* Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
*
* @param tag XML tag name
* @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</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 &lt;tag&gt;value&lt;/tag&gt; to output stream
*
* @param tag XML tag name
* @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
*/
public void writeTag(String tag, double value) {
writeTag(tag, Util.formatDouble(value, true));
}
/**
* Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
*
* @param tag XML tag name
* @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
*/
public void writeTag(String tag, boolean value) {
writeTag(tag, "" + value);
}
/**
* Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
*
* @param tag XML tag name
* @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
*/
public void writeTag(String tag, Color value) {
int rgb = value.getRGB() & 0xFFFFFF;
writeTag(tag, "#" + Integer.toHexString(rgb).toUpperCase());
}
/**
* Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
*
* @param tag XML tag name
* @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</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 &lt;tag&gt;value&lt;/tag&gt; to output stream
*
* @param tag XML tag name
* @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</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("<", "&lt;").replaceAll(">", "&gt;");
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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};
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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]);
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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])
};
}
}

View File

@ -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 + "]";
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -1,5 +0,0 @@
<html>
<body>
JRobin data management.
</body>
</html>

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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 org.jrobin.data.DataProcessor;
import org.jrobin.data.Plottable;
class PDef extends Source {
private Plottable plottable;
PDef(String name, Plottable plottable) {
super(name);
this.plottable = plottable;
}
void requestData(DataProcessor dproc) {
dproc.addDatasource(name, plottable);
}
}

View File

@ -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.graph;
class PathIterator {
private double[] y;
private int pos = 0;
PathIterator(double[] y) {
this.y = y;
}
int[] getNextPath() {
while (pos < y.length) {
if (Double.isNaN(y[pos])) {
pos++;
}
else {
int endPos = pos + 1;
while (endPos < y.length && !Double.isNaN(y[endPos])) {
endPos++;
}
int[] result = {pos, endPos};
pos = endPos;
if (result[1] - result[0] >= 2) {
return result;
}
}
}
return null;
}
}

View File

@ -1,46 +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.graph;
import org.jrobin.data.DataProcessor;
import org.jrobin.graph.Source;
public class PercentileDef extends Source {
private String m_sourceName;
private double m_percentile;
private boolean m_includenan;
PercentileDef(String name, String sourceName, double percentile) {
this(name, sourceName, percentile, false);
}
PercentileDef(String name, String sourceName, double percentile, boolean includenan) {
super(name);
m_sourceName = sourceName;
m_percentile = percentile;
m_includenan = includenan;
}
@Override
void requestData(DataProcessor dproc) {
dproc.addDatasource(name, m_sourceName, m_percentile, m_includenan);
}
}

View File

@ -1,29 +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 PlotElement {
final Paint color;
PlotElement(Paint color) {
this.color = color;
}
}

Some files were not shown because too many files have changed in this diff Show More