View Javadoc

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 }