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 }
272