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