/*******************************************************************************
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
* Copyright (c) 2011 The OpenNMS Group, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*******************************************************************************/
package org.jrobin.core;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
/**
* Class used to create an arbitrary number of {@link RrdDef} (RRD definition) objects
* from a single XML template. XML template can be supplied as an XML InputSource,
* XML file or XML formatted string.
* <p>
* Here is an example of a properly formatted XML template with all available
* options in it (unwanted options can be removed):
* <pre>
* <rrd_def>
* <path>test.rrd</path>
* <!-- not mandatory -->
* <start>1000123456</start>
* <!-- not mandatory -->
* <step>300</step>
* <!-- at least one datasource must be supplied -->
* <datasource>
* <name>input</name>
* <type>COUNTER</type>
* <heartbeat>300</heartbeat>
* <min>0</min>
* <max>U</max>
* </datasource>
* <datasource>
* <name>temperature</name>
* <type>GAUGE</type>
* <heartbeat>400</heartbeat>
* <min>U</min>
* <max>1000</max>
* </datasource>
* <!-- at least one archive must be supplied -->
* <archive>
* <cf>AVERAGE</cf>
* <xff>0.5</xff>
* <steps>1</steps>
* <rows>600</rows>
* </archive>
* <archive>
* <cf>MAX</cf>
* <xff>0.6</xff>
* <steps>6</steps>
* <rows>7000</rows>
* </archive>
* </rrd_def>
* </pre>
* Notes on the template syntax:<p>
* <ul>
* <li>There is a strong relation between the XML template syntax and the syntax of
* {@link RrdDef} class methods. If you are not sure what some XML tag means, check javadoc
* for the corresponding class.
* <li>starting timestamp can be supplied either as a long integer
* (like: 1000243567) or as an ISO formatted string (like: 2004-02-21 12:25:45)
* <li>whitespaces are not harmful
* <li>floating point values: anything that cannot be parsed will be treated as Double.NaN
* (like: U, unknown, 12r.23)
* <li>comments are allowed.
* </ul>
* Any template value (text between <code><some_tag></code> and
* <code></some_tag></code>) can be replaced with
* a variable of the following form: <code>${variable_name}</code>. Use
* {@link XmlTemplate#setVariable(String, String) setVariable()}
* methods from the base class to replace template variables with real values
* at runtime.
* <p>
* Typical usage scenario:<p>
* <ul>
* <li>Create your XML template and save it to a file (template.xml, for example)
* <li>Replace hardcoded template values with variables if you want to change them during runtime.
* For example, RRD path should not be hardcoded in the template - you probably want to create
* many different RRD files from the same XML template. For example, your XML
* template could start with:
* <pre>
* <rrd_def>
* <path>${path}</path>
* <step>300</step>
* ...
* </pre>
* <li>In your Java code, create RrdDefTemplate object using your XML template file:
* <pre>
* RrdDefTemplate t = new RrdDefTemplate(new File(template.xml));
* </pre>
* <li>Then, specify real values for template variables:
* <pre>
* t.setVariable("path", "demo/test.rrd");
* </pre>
* <li>Once all template variables are set, just use the template object to create RrdDef
* object. This object is actually used to create JRobin RRD files:
* <pre>
* RrdDef def = t.getRrdDef();
* RrdDb rrd = new RrdDb(def);
* rrd.close();
* </pre>
* </ul>
* You should create new RrdDefTemplate object only once for each XML template. Single template
* object can be reused to create as many RrdDef objects as needed, with different values
* specified for template variables. XML synatax check is performed only once - the first
* definition object gets created relatively slowly, but it will be created much faster next time.
*/
public class RrdDefTemplate extends XmlTemplate {
/**
* Creates RrdDefTemplate object from any parsable XML input source. Read general information
* for this class to find an example of a properly formatted RrdDef XML source.
*
* @param xmlInputSource Xml input source
* @throws IOException Thrown in case of I/O error
* @throws RrdException Thrown in case of XML related error (parsing error, for example)
*/
public RrdDefTemplate(InputSource xmlInputSource) throws IOException, RrdException {
super(xmlInputSource);
}
/**
* Creates RrdDefTemplate object from the string containing XML template.
* Read general information for this class to see an example of a properly formatted XML source.
*
* @param xmlString String containing XML template
* @throws IOException Thrown in case of I/O error
* @throws RrdException Thrown in case of XML related error (parsing error, for example)
*/
public RrdDefTemplate(String xmlString) throws IOException, RrdException {
super(xmlString);
}
/**
* Creates RrdDefTemplate object from the file containing XML template.
* Read general information for this class to see an example of a properly formatted XML source.
*
* @param xmlFile File object representing file with XML template
* @throws IOException Thrown in case of I/O error
* @throws RrdException Thrown in case of XML related error (parsing error, for example)
*/
public RrdDefTemplate(File xmlFile) throws IOException, RrdException {
super(xmlFile);
}
/**
* Returns RrdDef object constructed from the underlying XML template. Before this method
* is called, values for all non-optional placeholders must be supplied. To specify
* placeholder values at runtime, use some of the overloaded
* {@link XmlTemplate#setVariable(String, String) setVariable()} methods. Once this method
* returns, all placeholder values are preserved. To remove them all, call inhereted
* {@link XmlTemplate#clearValues() clearValues()} method explicitly.<p>
*
* @return RrdDef object constructed from the underlying XML template,
* with all placeholders replaced with real values. This object can be passed to the constructor
* of the new RrdDb object.
* @throws RrdException Thrown (in most cases) if the value for some placeholder
* was not supplied through {@link XmlTemplate#setVariable(String, String) setVariable()}
* method call
*/
public RrdDef getRrdDef() throws RrdException {
if (!root.getTagName().equals("rrd_def")) {
throw new RrdException("XML definition must start with <rrd_def>");
}
validateTagsOnlyOnce(root, new String[] {
"path", "start", "step", "datasource*", "archive*"
});
// PATH must be supplied or exception is thrown
String path = getChildValue(root, "path");
RrdDef rrdDef = new RrdDef(path);
try {
String startStr = getChildValue(root, "start");
Calendar startGc = Util.getCalendar(startStr);
rrdDef.setStartTime(startGc);
}
catch (RrdException e) {
// START is not mandatory
}
try {
long step = getChildValueAsLong(root, "step");
rrdDef.setStep(step);
}
catch (RrdException e) {
// STEP is not mandatory
}
// datsources
Node[] dsNodes = getChildNodes(root, "datasource");
for (Node dsNode : dsNodes) {
validateTagsOnlyOnce(dsNode, new String[] {
"name", "type", "heartbeat", "min", "max"
});
String name = getChildValue(dsNode, "name");
String type = getChildValue(dsNode, "type");
long heartbeat = getChildValueAsLong(dsNode, "heartbeat");
double min = getChildValueAsDouble(dsNode, "min");
double max = getChildValueAsDouble(dsNode, "max");
rrdDef.addDatasource(name, type, heartbeat, min, max);
}
// archives
Node[] arcNodes = getChildNodes(root, "archive");
for (Node arcNode : arcNodes) {
validateTagsOnlyOnce(arcNode, new String[] {
"cf", "xff", "steps", "rows"
});
String consolFun = getChildValue(arcNode, "cf");
double xff = getChildValueAsDouble(arcNode, "xff");
int steps = getChildValueAsInt(arcNode, "steps");
int rows = getChildValueAsInt(arcNode, "rows");
rrdDef.addArchive(consolFun, xff, steps, rows);
}
return rrdDef;
}
}