1 /* ============================================================ 2 * JRobin : Pure java implementation of RRDTool's functionality 3 * ============================================================ 4 * 5 * Project Info: http://www.jrobin.org 6 * Project Lead: Sasa Markovic (saxon@jrobin.org); 7 * 8 * (C) Copyright 2003-2005, by Sasa Markovic. 9 * 10 * Developers: Sasa Markovic (saxon@jrobin.org) 11 * 12 * 13 * This library is free software; you can redistribute it and/or modify it under the terms 14 * of the GNU Lesser General Public License as published by the Free Software Foundation; 15 * either version 2.1 of the License, or (at your option) any later version. 16 * 17 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 18 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 19 * See the GNU Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public License along with this 22 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 23 * Boston, MA 02111-1307, USA. 24 */ 25 package org.jrobin.core; 26 27 import org.w3c.dom.Node; 28 import org.xml.sax.InputSource; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.util.Calendar; 33 34 /** 35 * Class used to create an arbitrary number of {@link RrdDef} (RRD definition) objects 36 * from a single XML template. XML template can be supplied as an XML InputSource, 37 * XML file or XML formatted string.<p> 38 * <p/> 39 * Here is an example of a properly formatted XML template with all available 40 * options in it (unwanted options can be removed):<p> 41 * <pre> 42 * <rrd_def> 43 * <path>test.rrd</path> 44 * <!-- not mandatory --> 45 * <start>1000123456</start> 46 * <!-- not mandatory --> 47 * <step>300</step> 48 * <!-- at least one datasource must be supplied --> 49 * <datasource> 50 * <name>input</name> 51 * <type>COUNTER</type> 52 * <heartbeat>300</heartbeat> 53 * <min>0</min> 54 * <max>U</max> 55 * </datasource> 56 * <datasource> 57 * <name>temperature</name> 58 * <type>GAUGE</type> 59 * <heartbeat>400</heartbeat> 60 * <min>U</min> 61 * <max>1000</max> 62 * </datasource> 63 * <!-- at least one archive must be supplied --> 64 * <archive> 65 * <cf>AVERAGE</cf> 66 * <xff>0.5</xff> 67 * <steps>1</steps> 68 * <rows>600</rows> 69 * </archive> 70 * <archive> 71 * <cf>MAX</cf> 72 * <xff>0.6</xff> 73 * <steps>6</steps> 74 * <rows>7000</rows> 75 * </archive> 76 * </rrd_def> 77 * </pre> 78 * Notes on the template syntax:<p> 79 * <ul> 80 * <li>There is a strong relation between the XML template syntax and the syntax of 81 * {@link RrdDef} class methods. If you are not sure what some XML tag means, check javadoc 82 * for the corresponding class. 83 * <li>starting timestamp can be supplied either as a long integer 84 * (like: 1000243567) or as an ISO formatted string (like: 2004-02-21 12:25:45) 85 * <li>whitespaces are not harmful 86 * <li>floating point values: anything that cannot be parsed will be treated as Double.NaN 87 * (like: U, unknown, 12r.23) 88 * <li>comments are allowed. 89 * </ul> 90 * Any template value (text between <code><some_tag></code> and 91 * <code></some_tag></code>) can be replaced with 92 * a variable of the following form: <code>${variable_name}</code>. Use 93 * {@link XmlTemplate#setVariable(String, String) setVariable()} 94 * methods from the base class to replace template variables with real values 95 * at runtime.<p> 96 * <p/> 97 * Typical usage scenario:<p> 98 * <ul> 99 * <li>Create your XML template and save it to a file (template.xml, for example) 100 * <li>Replace hardcoded template values with variables if you want to change them during runtime. 101 * For example, RRD path should not be hardcoded in the template - you probably want to create 102 * many different RRD files from the same XML template. For example, your XML 103 * template could start with: 104 * <pre> 105 * <rrd_def> 106 * <path>${path}</path> 107 * <step>300</step> 108 * ... 109 * </pre> 110 * <li>In your Java code, create RrdDefTemplate object using your XML template file: 111 * <pre> 112 * RrdDefTemplate t = new RrdDefTemplate(new File(template.xml)); 113 * </pre> 114 * <li>Then, specify real values for template variables: 115 * <pre> 116 * t.setVariable("path", "demo/test.rrd"); 117 * </pre> 118 * <li>Once all template variables are set, just use the template object to create RrdDef 119 * object. This object is actually used to create JRobin RRD files: 120 * <pre> 121 * RrdDef def = t.getRrdDef(); 122 * RrdDb rrd = new RrdDb(def); 123 * rrd.close(); 124 * </pre> 125 * </ul> 126 * You should create new RrdDefTemplate object only once for each XML template. Single template 127 * object can be reused to create as many RrdDef objects as needed, with different values 128 * specified for template variables. XML synatax check is performed only once - the first 129 * definition object gets created relatively slowly, but it will be created much faster next time. 130 */ 131 public class RrdDefTemplate extends XmlTemplate { 132 /** 133 * Creates RrdDefTemplate object from any parsable XML input source. Read general information 134 * for this class to find an example of a properly formatted RrdDef XML source. 135 * 136 * @param xmlInputSource Xml input source 137 * @throws IOException Thrown in case of I/O error 138 * @throws RrdException Thrown in case of XML related error (parsing error, for example) 139 */ 140 public RrdDefTemplate(InputSource xmlInputSource) throws IOException, RrdException { 141 super(xmlInputSource); 142 } 143 144 /** 145 * Creates RrdDefTemplate object from the string containing XML template. 146 * Read general information for this class to see an example of a properly formatted XML source. 147 * 148 * @param xmlString String containing XML template 149 * @throws IOException Thrown in case of I/O error 150 * @throws RrdException Thrown in case of XML related error (parsing error, for example) 151 */ 152 public RrdDefTemplate(String xmlString) throws IOException, RrdException { 153 super(xmlString); 154 } 155 156 /** 157 * Creates RrdDefTemplate object from the file containing XML template. 158 * Read general information for this class to see an example of a properly formatted XML source. 159 * 160 * @param xmlFile File object representing file with XML template 161 * @throws IOException Thrown in case of I/O error 162 * @throws RrdException Thrown in case of XML related error (parsing error, for example) 163 */ 164 public RrdDefTemplate(File xmlFile) throws IOException, RrdException { 165 super(xmlFile); 166 } 167 168 /** 169 * Returns RrdDef object constructed from the underlying XML template. Before this method 170 * is called, values for all non-optional placeholders must be supplied. To specify 171 * placeholder values at runtime, use some of the overloaded 172 * {@link XmlTemplate#setVariable(String, String) setVariable()} methods. Once this method 173 * returns, all placeholder values are preserved. To remove them all, call inhereted 174 * {@link XmlTemplate#clearValues() clearValues()} method explicitly.<p> 175 * 176 * @return RrdDef object constructed from the underlying XML template, 177 * with all placeholders replaced with real values. This object can be passed to the constructor 178 * of the new RrdDb object. 179 * @throws RrdException Thrown (in most cases) if the value for some placeholder 180 * was not supplied through {@link XmlTemplate#setVariable(String, String) setVariable()} 181 * method call 182 */ 183 public RrdDef getRrdDef() throws RrdException { 184 if (!root.getTagName().equals("rrd_def")) { 185 throw new RrdException("XML definition must start with <rrd_def>"); 186 } 187 validateTagsOnlyOnce(root, new String[] { 188 "path", "start", "step", "datasource*", "archive*" 189 }); 190 // PATH must be supplied or exception is thrown 191 String path = getChildValue(root, "path"); 192 RrdDef rrdDef = new RrdDef(path); 193 try { 194 String startStr = getChildValue(root, "start"); 195 Calendar startGc = Util.getCalendar(startStr); 196 rrdDef.setStartTime(startGc); 197 } 198 catch (RrdException e) { 199 // START is not mandatory 200 } 201 try { 202 long step = getChildValueAsLong(root, "step"); 203 rrdDef.setStep(step); 204 } 205 catch (RrdException e) { 206 // STEP is not mandatory 207 } 208 // datsources 209 Node[] dsNodes = getChildNodes(root, "datasource"); 210 for (Node dsNode : dsNodes) { 211 validateTagsOnlyOnce(dsNode, new String[] { 212 "name", "type", "heartbeat", "min", "max" 213 }); 214 String name = getChildValue(dsNode, "name"); 215 String type = getChildValue(dsNode, "type"); 216 long heartbeat = getChildValueAsLong(dsNode, "heartbeat"); 217 double min = getChildValueAsDouble(dsNode, "min"); 218 double max = getChildValueAsDouble(dsNode, "max"); 219 rrdDef.addDatasource(name, type, heartbeat, min, max); 220 } 221 // archives 222 Node[] arcNodes = getChildNodes(root, "archive"); 223 for (Node arcNode : arcNodes) { 224 validateTagsOnlyOnce(arcNode, new String[] { 225 "cf", "xff", "steps", "rows" 226 }); 227 String consolFun = getChildValue(arcNode, "cf"); 228 double xff = getChildValueAsDouble(arcNode, "xff"); 229 int steps = getChildValueAsInt(arcNode, "steps"); 230 int rows = getChildValueAsInt(arcNode, "rows"); 231 rrdDef.addArchive(consolFun, xff, steps, rows); 232 } 233 return rrdDef; 234 } 235 }