View Javadoc

1   /*
2    * Copyright (C) 2001 Ciaran Treanor <ciaran@codeloop.com>
3    *
4    * Distributable under GPL license.
5    * See terms of license at gnu.org.
6    *
7    * $Id: Archive.java,v 1.3 2006/12/21 18:02:42 tarus Exp $
8    */
9   package org.jrobin.core.jrrd;
10  
11  import java.io.IOException;
12  import java.io.PrintStream;
13  import java.text.DecimalFormat;
14  import java.text.NumberFormat;
15  import java.text.SimpleDateFormat;
16  import java.util.ArrayList;
17  import java.util.Calendar;
18  import java.util.Date;
19  import java.util.Iterator;
20  
21  /**
22   * Instances of this class model an archive section of an RRD file.
23   *
24   * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
25   * @version $Revision: 1.3 $
26   */
27  public class Archive {
28  
29  	RRDatabase db;
30  	long offset;
31  	long dataOffset;
32  	long size;
33  	ConsolidationFunctionType type;
34  	int rowCount;
35  	int pdpCount;
36  	double xff;
37  	ArrayList<CDPStatusBlock> cdpStatusBlocks;
38  	int currentRow;
39  
40  	private double[][] values;
41  
42  	Archive(RRDatabase db) throws IOException {
43  
44  		this.db = db;
45  
46  		RRDFile file = db.rrdFile;
47  
48  		offset = file.getFilePointer();
49  		type =
50  				ConsolidationFunctionType.get(file.readString(Constants.CF_NAM_SIZE));
51  		rowCount = file.readInt();
52  		pdpCount = file.readInt();
53  
54  		file.align();
55  
56  		xff = file.readDouble();
57  
58  		// Skip rest of rra_def_t.par[]
59  		file.align();
60  		file.skipBytes(72);
61  
62  		size = file.getFilePointer() - offset;
63  	}
64  
65  	/**
66  	 * Returns the type of function used to calculate the consolidated data point.
67  	 *
68  	 * @return the type of function used to calculate the consolidated data point.
69  	 */
70  	public ConsolidationFunctionType getType() {
71  		return type;
72  	}
73  
74  	void loadCDPStatusBlocks(RRDFile file, int numBlocks) throws IOException {
75  
76  		cdpStatusBlocks = new ArrayList<CDPStatusBlock>();
77  
78  		for (int i = 0; i < numBlocks; i++) {
79  			cdpStatusBlocks.add(new CDPStatusBlock(file));
80  		}
81  	}
82  
83  	/**
84  	 * Returns the <code>CDPStatusBlock</code> at the specified position in this archive.
85  	 *
86  	 * @param index index of <code>CDPStatusBlock</code> to return.
87  	 * @return the <code>CDPStatusBlock</code> at the specified position in this archive.
88  	 */
89  	public CDPStatusBlock getCDPStatusBlock(int index) {
90  		return cdpStatusBlocks.get(index);
91  	}
92  
93  	/**
94  	 * Returns an iterator over the CDP status blocks in this archive in proper sequence.
95  	 *
96  	 * @return an iterator over the CDP status blocks in this archive in proper sequence.
97  	 * @see CDPStatusBlock
98  	 */
99  	public Iterator<CDPStatusBlock> getCDPStatusBlocks() {
100 		return cdpStatusBlocks.iterator();
101 	}
102 
103 	void loadCurrentRow(RRDFile file) throws IOException {
104 		currentRow = file.readInt();
105 	}
106 
107 	void loadData(RRDFile file, int dsCount) throws IOException {
108 
109 		dataOffset = file.getFilePointer();
110 
111 		// Skip over the data to position ourselves at the start of the next archive
112 		file.skipBytes(8 * rowCount * dsCount);
113 	}
114 
115 	DataChunk loadData(DataChunk chunk) throws IOException {
116 
117 		Calendar end = Calendar.getInstance();
118 		Calendar start = (Calendar) end.clone();
119 
120 		start.add(Calendar.DATE, -1);
121 
122 		loadData(chunk, start.getTime().getTime() / 1000,
123 				end.getTime().getTime() / 1000);
124 		return chunk;
125 	}
126 
127 	void loadData(DataChunk chunk, long startTime, long endTime)
128 			throws IOException {
129 
130 		long pointer;
131 
132 		if (chunk.start < 0) {
133 			pointer = currentRow + 1;
134 		}
135 		else {
136 			pointer = currentRow + chunk.start + 1;
137 		}
138 
139 		db.rrdFile.ras.seek(dataOffset + (pointer * 8));
140 		//cat.debug("Archive Base: " + dataOffset + " Archive Pointer: " + pointer);
141 		//cat.debug("Start Offset: " + chunk.start + " End Offset: "
142 		//          + (rowCount - chunk.end));
143 
144 		double[][] data = chunk.data;
145 
146 		/*
147 		 * This is also terrible - cleanup - CT
148 		 */
149 		int row = 0;
150 		for (int i = chunk.start; i < rowCount - chunk.end; i++, row++) {
151 			if (i < 0) {				   // no valid data yet
152 				for (int ii = 0; ii < chunk.dsCount; ii++) {
153 					data[row][ii] = Double.NaN;
154 				}
155 			}
156 			else if (i >= rowCount) {	// past valid data area
157 				for (int ii = 0; ii < chunk.dsCount; ii++) {
158 					data[row][ii] = Double.NaN;
159 				}
160 			}
161 			else {					   // inside the valid are but the pointer has to be wrapped
162 				if (pointer >= rowCount) {
163 					pointer -= rowCount;
164 
165 					db.rrdFile.ras.seek(dataOffset + (pointer * 8));
166 				}
167 
168 				for (int ii = 0; ii < chunk.dsCount; ii++) {
169 					data[row][ii] = db.rrdFile.readDouble();
170 				}
171 
172 				pointer++;
173 			}
174 		}
175 	}
176 
177 	void printInfo(PrintStream s, NumberFormat numberFormat, int index) {
178 
179 		StringBuffer sb = new StringBuffer("rra[");
180 
181 		sb.append(index);
182 		s.print(sb);
183 		s.print("].cf = \"");
184 		s.print(type);
185 		s.println("\"");
186 		s.print(sb);
187 		s.print("].rows = ");
188 		s.println(rowCount);
189 		s.print(sb);
190 		s.print("].pdp_per_row = ");
191 		s.println(pdpCount);
192 		s.print(sb);
193 		s.print("].xff = ");
194 		s.println(xff);
195 		sb.append("].cdp_prep[");
196 
197 		int cdpIndex = 0;
198 
199 		for (Iterator<CDPStatusBlock> i = cdpStatusBlocks.iterator(); i.hasNext();) {
200 			CDPStatusBlock cdp = i.next();
201 
202 			s.print(sb);
203 			s.print(cdpIndex);
204 			s.print("].value = ");
205 
206 			double value = cdp.value;
207 
208 			s.println(Double.isNaN(value)
209 					? "NaN"
210 					: numberFormat.format(value));
211 			s.print(sb);
212 			s.print(cdpIndex++);
213 			s.print("].unknown_datapoints = ");
214 			s.println(cdp.unknownDatapoints);
215 		}
216 	}
217 
218 	void toXml(PrintStream s) {
219 
220 		try {
221 			s.println("\t<rra>");
222 			s.print("\t\t<cf> ");
223 			s.print(type);
224 			s.println(" </cf>");
225 			s.print("\t\t<pdp_per_row> ");
226 			s.print(pdpCount);
227 			s.print(" </pdp_per_row> <!-- ");
228 			s.print(db.header.pdpStep * pdpCount);
229 			s.println(" seconds -->");
230 			s.print("\t\t<xff> ");
231 			s.print(xff);
232 			s.println(" </xff>");
233 			s.println();
234 			s.println("\t\t<cdp_prep>");
235 
236 			for (int i = 0; i < cdpStatusBlocks.size(); i++) {
237 				cdpStatusBlocks.get(i).toXml(s);
238 			}
239 
240 			s.println("\t\t</cdp_prep>");
241 			s.println("\t\t<database>");
242 
243 			long timer = -(rowCount - 1);
244 			int counter = 0;
245 			int row = currentRow;
246 
247 			db.rrdFile.ras.seek(dataOffset + (row + 1) * 16);
248 
249 			long lastUpdate = db.lastUpdate.getTime() / 1000;
250 			int pdpStep = db.header.pdpStep;
251 			NumberFormat numberFormat = new DecimalFormat("0.0000000000E0");
252 			SimpleDateFormat dateFormat =
253 					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
254 
255 			while (counter++ < rowCount) {
256 				row++;
257 
258 				if (row == rowCount) {
259 					row = 0;
260 
261 					db.rrdFile.ras.seek(dataOffset);
262 				}
263 
264 				long now = (lastUpdate - lastUpdate % (pdpCount * pdpStep))
265 						+ (timer * pdpCount * pdpStep);
266 
267 				timer++;
268 
269 				s.print("\t\t\t<!-- ");
270 				s.print(dateFormat.format(new Date(now * 1000)));
271 				s.print(" / ");
272 				s.print(now);
273 				s.print(" --> ");
274 
275 				for (int col = 0; col < db.header.dsCount; col++) {
276 					s.print("<v> ");
277 
278 					double value = db.rrdFile.readDouble();
279 
280 					// NumberFormat doesn't know how to handle NaN
281 					if (Double.isNaN(value)) {
282 						s.print("NaN");
283 					}
284 					else {
285 						s.print(numberFormat.format(value));
286 					}
287 
288 					s.print(" </v>");
289 				}
290 
291 				s.println("</row>");
292 			}
293 
294 			s.println("\t\t</database>");
295 			s.println("\t</rra>");
296 		}
297 		catch (IOException e) {	// Is the best thing to do here?
298 			throw new RuntimeException(e.getMessage());
299 		}
300 	}
301 
302 	/*
303 	// THIS IS THE ORIGINAL CODE: BUGGY! Replaced by Sasa Markovic with a new method
304 	// Funny: the bug will appear only if dsCount != 2 :)
305 	public double[][] getValuesOriginal() throws IOException {
306 		if (values != null) {
307 			return values;
308 		}
309 		values = new double[db.header.dsCount][rowCount];
310 		int row = currentRow;
311 		db.rrdFile.ras.seek(dataOffset + (row + 1) * 16); // <----- BUG (resolved below)
312 		for (int counter = 0; counter < rowCount; counter++) {
313 			row++;
314 			if (row == rowCount) {
315 				row = 0;
316 				db.rrdFile.ras.seek(dataOffset);
317 			}
318 			for (int col = 0; col < db.header.dsCount; col++) {
319 				double value = db.rrdFile.readDouble();
320 				values[col][counter] = value;
321 			}
322 		}
323 		return values;
324 	}
325     */
326 
327 	// Resolved bug from the original method (see above)
328 	public double[][] getValues() throws IOException {
329 		// OK PART
330 		if (values != null) {
331 			return values;
332 		}
333 		values = new double[db.header.dsCount][rowCount];
334 		int row = currentRow;
335 		// HERE ARE THE DRAGONS!
336 		db.rrdFile.ras.seek(dataOffset + (row + 1) * db.header.dsCount * 8);
337 		// OK, TOO!
338 		for (int counter = 0; counter < rowCount; counter++) {
339 			row++;
340 			if (row == rowCount) {
341 				row = 0;
342 				db.rrdFile.ras.seek(dataOffset);
343 			}
344 			for (int col = 0; col < db.header.dsCount; col++) {
345 				double value = db.rrdFile.readDouble();
346 				values[col][counter] = value;
347 			}
348 		}
349 		return values;
350 	}
351 
352 	/**
353 	 * Returns the number of primary data points required for a consolidated
354 	 * data point in this archive.
355 	 *
356 	 * @return the number of primary data points required for a consolidated
357 	 *         data point in this archive.
358 	 */
359 	public int getPdpCount() {
360 		return pdpCount;
361 	}
362 
363 	/**
364 	 * Returns the number of entries in this archive.
365 	 *
366 	 * @return the number of entries in this archive.
367 	 */
368 	public int getRowCount() {
369 		return rowCount;
370 	}
371 
372 	/**
373 	 * Returns the X-Files Factor for this archive.
374 	 *
375 	 * @return the X-Files Factor for this archive.
376 	 */
377 	public double getXff() {
378 		return xff;
379 	}
380 
381 	/**
382 	 * Returns a summary the contents of this archive.
383 	 *
384 	 * @return a summary of the information contained in this archive.
385 	 */
386 	public String toString() {
387 
388 		StringBuffer sb = new StringBuffer("[Archive: OFFSET=0x");
389 
390 		sb.append(Long.toHexString(offset));
391 		sb.append(", SIZE=0x");
392 		sb.append(Long.toHexString(size));
393 		sb.append(", type=");
394 		sb.append(type);
395 		sb.append(", rowCount=");
396 		sb.append(rowCount);
397 		sb.append(", pdpCount=");
398 		sb.append(pdpCount);
399 		sb.append(", xff=");
400 		sb.append(xff);
401 		sb.append(", currentRow=");
402 		sb.append(currentRow);
403 		sb.append("]");
404 
405 		for (Iterator<CDPStatusBlock> i = cdpStatusBlocks.iterator(); i.hasNext();) {
406 			CDPStatusBlock cdp = i.next();
407 
408 			sb.append("\n\t\t");
409 			sb.append(cdp.toString());
410 		}
411 
412 		return sb.toString();
413 	}
414 }