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 26 package org.jrobin.core; 27 28 import java.io.IOException; 29 import java.util.StringTokenizer; 30 31 /** 32 * <p>Class to represent data source values for the given timestamp. Objects of this 33 * class are never created directly (no public constructor is provided). To learn more how 34 * to update RRDs, see RRDTool's 35 * <a href="../../../../man/rrdupdate.html" target="man">rrdupdate man page</a>. 36 * <p/> 37 * <p>To update a RRD with JRobin use the following procedure:</p> 38 * <p/> 39 * <ol> 40 * <li>Obtain empty Sample object by calling method {@link RrdDb#createSample(long) 41 * createSample()} on respective {@link RrdDb RrdDb} object. 42 * <li>Adjust Sample timestamp if necessary (see {@link #setTime(long) setTime()} method). 43 * <li>Supply data source values (see {@link #setValue(String, double) setValue()}). 44 * <li>Call Sample's {@link #update() update()} method. 45 * </ol> 46 * <p/> 47 * <p>Newly created Sample object contains all data source values set to 'unknown'. 48 * You should specifify only 'known' data source values. However, if you want to specify 49 * 'unknown' values too, use <code>Double.NaN</code>.</p> 50 * 51 * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a> 52 */ 53 public class Sample { 54 private RrdDb parentDb; 55 private long time; 56 private String[] dsNames; 57 private double[] values; 58 59 Sample(RrdDb parentDb, long time) throws IOException { 60 this.parentDb = parentDb; 61 this.time = time; 62 this.dsNames = parentDb.getDsNames(); 63 values = new double[dsNames.length]; 64 clearCurrentValues(); 65 } 66 67 private Sample clearCurrentValues() { 68 for (int i = 0; i < values.length; i++) { 69 values[i] = Double.NaN; 70 } 71 return this; 72 } 73 74 /** 75 * Sets single data source value in the sample. 76 * 77 * @param dsName Data source name. 78 * @param value Data source value. 79 * @return This <code>Sample</code> object 80 * @throws RrdException Thrown if invalid data source name is supplied. 81 */ 82 public Sample setValue(String dsName, double value) throws RrdException { 83 for (int i = 0; i < values.length; i++) { 84 if (dsNames[i].equals(dsName)) { 85 values[i] = value; 86 return this; 87 } 88 } 89 throw new RrdException("Datasource " + dsName + " not found"); 90 } 91 92 /** 93 * Sets single datasource value using data source index. Data sources are indexed by 94 * the order specified during RRD creation (zero-based). 95 * 96 * @param i Data source index 97 * @param value Data source values 98 * @return This <code>Sample</code> object 99 * @throws RrdException Thrown if data source index is invalid. 100 */ 101 public Sample setValue(int i, double value) throws RrdException { 102 if (i < values.length) { 103 values[i] = value; 104 return this; 105 } 106 else { 107 throw new RrdException("Sample datasource index " + i + " out of bounds"); 108 } 109 } 110 111 /** 112 * Sets some (possibly all) data source values in bulk. Data source values are 113 * assigned in the order of their definition inside the RRD. 114 * 115 * @param values Data source values. 116 * @return This <code>Sample</code> object 117 * @throws RrdException Thrown if the number of supplied values is zero or greater 118 * than the number of data sources defined in the RRD. 119 */ 120 public Sample setValues(double[] values) throws RrdException { 121 if (values.length <= this.values.length) { 122 System.arraycopy(values, 0, this.values, 0, values.length); 123 return this; 124 } 125 else { 126 throw new RrdException("Invalid number of values specified (found " + 127 values.length + ", only " + dsNames.length + " allowed)"); 128 } 129 } 130 131 /** 132 * Returns all current data source values in the sample. 133 * 134 * @return Data source values. 135 */ 136 public double[] getValues() { 137 return values; 138 } 139 140 /** 141 * Returns sample timestamp (in seconds, without milliseconds). 142 * 143 * @return Sample timestamp. 144 */ 145 public long getTime() { 146 return time; 147 } 148 149 /** 150 * Sets sample timestamp. Timestamp should be defined in seconds (without milliseconds). 151 * 152 * @param time New sample timestamp. 153 * @return This <code>Sample</code> object 154 */ 155 public Sample setTime(long time) { 156 this.time = time; 157 return this; 158 } 159 160 /** 161 * Returns an array of all data source names. If you try to set value for the data source 162 * name not in this array, an exception is thrown. 163 * 164 * @return Acceptable data source names. 165 */ 166 public String[] getDsNames() { 167 return dsNames; 168 } 169 170 /** 171 * <p>Sets sample timestamp and data source values in a fashion similar to RRDTool. 172 * Argument string should be composed in the following way: 173 * <code>timestamp:value1:value2:...:valueN</code>.</p> 174 * <p/> 175 * <p>You don't have to supply all datasource values. Unspecified values will be treated 176 * as unknowns. To specify unknown value in the argument string, use letter 'U' 177 * 178 * @param timeAndValues String made by concatenating sample timestamp with corresponding 179 * data source values delmited with colons. For example:<p> 180 * <pre> 181 * 1005234132:12.2:35.6:U:24.5 182 * NOW:12.2:35.6:U:24.5 183 * </pre> 184 * 'N' stands for the current timestamp (can be replaced with 'NOW')<p> 185 * Method will throw an exception if timestamp is invalid (cannot be parsed as Long, and is not 'N' 186 * or 'NOW'). Datasource value which cannot be parsed as 'double' will be silently set to NaN.<p> 187 * @return This <code>Sample</code> object 188 * @throws RrdException Thrown if too many datasource values are supplied 189 */ 190 public Sample set(String timeAndValues) throws RrdException { 191 StringTokenizer tokenizer = new StringTokenizer(timeAndValues, ":", false); 192 int n = tokenizer.countTokens(); 193 if (n > values.length + 1) { 194 throw new RrdException("Invalid number of values specified (found " + 195 values.length + ", " + dsNames.length + " allowed)"); 196 } 197 String timeToken = tokenizer.nextToken(); 198 try { 199 time = Long.parseLong(timeToken); 200 } 201 catch (NumberFormatException nfe) { 202 if (timeToken.equalsIgnoreCase("N") || timeToken.equalsIgnoreCase("NOW")) { 203 time = Util.getTime(); 204 } 205 else { 206 throw new RrdException("Invalid sample timestamp: " + timeToken); 207 } 208 } 209 for (int i = 0; tokenizer.hasMoreTokens(); i++) { 210 try { 211 values[i] = Double.parseDouble(tokenizer.nextToken()); 212 } 213 catch (NumberFormatException nfe) { 214 // NOP, value is already set to NaN 215 } 216 } 217 return this; 218 } 219 220 /** 221 * Stores sample in the corresponding RRD. If the update operation succeedes, 222 * all datasource values in the sample will be set to Double.NaN (unknown) values. 223 * 224 * @throws IOException Thrown in case of I/O error. 225 * @throws RrdException Thrown in case of JRobin related error. 226 */ 227 public void update() throws IOException, RrdException { 228 parentDb.store(this); 229 clearCurrentValues(); 230 } 231 232 /** 233 * <p>Creates sample with the timestamp and data source values supplied 234 * in the argument string and stores sample in the corresponding RRD. 235 * This method is just a shortcut for:</p> 236 * <pre> 237 * set(timeAndValues); 238 * update(); 239 * </pre> 240 * 241 * @param timeAndValues String made by concatenating sample timestamp with corresponding 242 * data source values delmited with colons. For example:<br> 243 * <code>1005234132:12.2:35.6:U:24.5</code><br> 244 * <code>NOW:12.2:35.6:U:24.5</code> 245 * @throws IOException Thrown in case of I/O error. 246 * @throws RrdException Thrown in case of JRobin related error. 247 */ 248 public void setAndUpdate(String timeAndValues) throws IOException, RrdException { 249 set(timeAndValues); 250 update(); 251 } 252 253 /** 254 * Dumps sample content using the syntax of RRDTool's update command. 255 * 256 * @return Sample dump. 257 */ 258 public String dump() { 259 StringBuffer buffer = new StringBuffer("update \""); 260 buffer.append(parentDb.getRrdBackend().getPath()).append("\" ").append(time); 261 for (double value : values) { 262 buffer.append(":"); 263 buffer.append(Util.formatDouble(value, "U", false)); 264 } 265 return buffer.toString(); 266 } 267 268 String getRrdToolCommand() { 269 return dump(); 270 } 271 }