001 /* ============================================================
002 * JRobin : Pure java implementation of RRDTool's functionality
003 * ============================================================
004 *
005 * Project Info: http://www.jrobin.org
006 * Project Lead: Sasa Markovic (saxon@jrobin.org);
007 *
008 * (C) Copyright 2003-2005, by Sasa Markovic.
009 *
010 * Developers: Sasa Markovic (saxon@jrobin.org)
011 *
012 *
013 * This library is free software; you can redistribute it and/or modify it under the terms
014 * of the GNU Lesser General Public License as published by the Free Software Foundation;
015 * either version 2.1 of the License, or (at your option) any later version.
016 *
017 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
018 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
019 * See the GNU Lesser General Public License for more details.
020 *
021 * You should have received a copy of the GNU Lesser General Public License along with this
022 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
023 * Boston, MA 02111-1307, USA.
024 */
025 package org.jrobin.core;
026
027 import org.w3c.dom.Node;
028 import org.xml.sax.InputSource;
029
030 import java.io.File;
031 import java.io.IOException;
032 import java.util.Calendar;
033
034 /**
035 * Class used to create an arbitrary number of {@link RrdDef} (RRD definition) objects
036 * from a single XML template. XML template can be supplied as an XML InputSource,
037 * XML file or XML formatted string.<p>
038 * <p/>
039 * Here is an example of a properly formatted XML template with all available
040 * options in it (unwanted options can be removed):<p>
041 * <pre>
042 * <rrd_def>
043 * <path>test.rrd</path>
044 * <!-- not mandatory -->
045 * <start>1000123456</start>
046 * <!-- not mandatory -->
047 * <step>300</step>
048 * <!-- at least one datasource must be supplied -->
049 * <datasource>
050 * <name>input</name>
051 * <type>COUNTER</type>
052 * <heartbeat>300</heartbeat>
053 * <min>0</min>
054 * <max>U</max>
055 * </datasource>
056 * <datasource>
057 * <name>temperature</name>
058 * <type>GAUGE</type>
059 * <heartbeat>400</heartbeat>
060 * <min>U</min>
061 * <max>1000</max>
062 * </datasource>
063 * <!-- at least one archive must be supplied -->
064 * <archive>
065 * <cf>AVERAGE</cf>
066 * <xff>0.5</xff>
067 * <steps>1</steps>
068 * <rows>600</rows>
069 * </archive>
070 * <archive>
071 * <cf>MAX</cf>
072 * <xff>0.6</xff>
073 * <steps>6</steps>
074 * <rows>7000</rows>
075 * </archive>
076 * </rrd_def>
077 * </pre>
078 * Notes on the template syntax:<p>
079 * <ul>
080 * <li>There is a strong relation between the XML template syntax and the syntax of
081 * {@link RrdDef} class methods. If you are not sure what some XML tag means, check javadoc
082 * for the corresponding class.
083 * <li>starting timestamp can be supplied either as a long integer
084 * (like: 1000243567) or as an ISO formatted string (like: 2004-02-21 12:25:45)
085 * <li>whitespaces are not harmful
086 * <li>floating point values: anything that cannot be parsed will be treated as Double.NaN
087 * (like: U, unknown, 12r.23)
088 * <li>comments are allowed.
089 * </ul>
090 * Any template value (text between <code><some_tag></code> and
091 * <code></some_tag></code>) can be replaced with
092 * a variable of the following form: <code>${variable_name}</code>. Use
093 * {@link XmlTemplate#setVariable(String, String) setVariable()}
094 * methods from the base class to replace template variables with real values
095 * at runtime.<p>
096 * <p/>
097 * Typical usage scenario:<p>
098 * <ul>
099 * <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 }