KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jrobin > core > RrdDb


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, by Sasa Markovic.
9  *
10  * Developers: Sasa Markovic (saxon@jrobin.org)
11  * Arne Vandamme (cobralord@jrobin.org)
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.ByteArrayOutputStream JavaDoc;
29 import java.io.FileOutputStream JavaDoc;
30 import java.io.IOException JavaDoc;
31 import java.io.OutputStream JavaDoc;
32
33 /**
34  * <p>Main class used to create and manipulate round robin databases (RRDs). Use this class to perform
35  * update and fetch operations on exisiting RRDs, to create new RRD from
36  * the definition (object of class {@link org.jrobin.core.RrdDef RrdDef}) or
37  * from XML file (dumped content of RRDTool's or JRobin's RRD file).</p>
38  *
39  * <p>Each RRD is backed with some kind of storage. For example, RRDTool supports only one kind of
40  * storage (disk file). On the contrary, JRobin gives you freedom to use other storage (backend) types
41  * even to create your own backend types for some special purposes. JRobin by default stores
42  * RRD data in files (as RRDTool), but you might choose to store RRD data in memory (this is
43  * supported in JRobin), to use java.nio.* instead of java.io.* package for file manipulation
44  * (also supported) or to store whole RRDs in the SQL database
45  * (you'll have to extend some classes to do this).</p>
46  *
47  * <p>Note that JRobin uses binary format different from RRDTool's format. You cannot
48  * use this class to manipulate RRD files created with RRDTool. <b>However, if you perform
49  * the same sequence of create, update and fetch operations, you will get exactly the same
50  * results from JRobin and RRDTool.</b><p>
51  *
52  * <p>
53  * You will not be able to use JRobin API if you are not familiar with
54  * basic RRDTool concepts. Good place to start is the
55  * <a HREF="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/tutorial/rrdtutorial.html">official RRD tutorial</a>
56  * and relevant RRDTool man pages: <a HREF="../../../../man/rrdcreate.html" target="man">rrdcreate</a>,
57  * <a HREF="../../../../man/rrdupdate.html" target="man">rrdupdate</a>,
58  * <a HREF="../../../../man/rrdfetch.html" target="man">rrdfetch</a> and
59  * <a HREF="../../../../man/rrdgraph.html" target="man">rrdgraph</a>.
60  * For RRDTool's advanced graphing capabilities (RPN extensions), also supported in JRobin,
61  * there is an excellent
62  * <a HREF="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/tutorial/cdeftutorial.html" target="man">CDEF tutorial</a>.
63  * </p>
64  *
65  * @see RrdBackend
66  * @see RrdBackendFactory
67  */

68 public class RrdDb implements RrdUpdater {
69     /** See {@link #getLockMode() getLockMode()} for explanation */
70     public static final int NO_LOCKS = 0;
71     /** See {@link #getLockMode() getLockMode()} for explanation */
72     public static final int WAIT_IF_LOCKED = 1;
73     /** See {@link #getLockMode() getLockMode()} for explanation */
74     public static final int EXCEPTION_IF_LOCKED = 2;
75
76     // static final String RRDTOOL = "rrdtool";
77
static final int XML_INITIAL_BUFFER_CAPACITY = 100000; // bytes
78
private static int lockMode = NO_LOCKS;
79
80     private RrdBackend backend;
81     private RrdAllocator allocator = new RrdAllocator();
82
83     private Header header;
84     private Datasource[] datasources;
85     private Archive[] archives;
86
87     private boolean closed = false;
88
89     /**
90      * <p>Constructor used to create new RRD object from the definition. This RRD object will be backed
91      * with a storage (backend) of the default type. Initially, storage type defaults to "NIO"
92      * (RRD bytes will be put in a file on the disk). Default storage type can be changed with a static
93      * {@link RrdBackendFactory#setDefaultFactory(String)} method call.</p>
94      *
95      * <p>New RRD file structure is specified with an object of class
96      * {@link org.jrobin.core.RrdDef <b>RrdDef</b>}. The underlying RRD storage is created as soon
97      * as the constructor returns.</p>
98      *
99      * <p>Typical scenario:</p>
100      *
101      * <pre>
102      * // create new RRD definition
103      * RrdDef def = new RrdDef("test.rrd", 300);
104      * def.addDatasource("input", "COUNTER", 600, 0, Double.NaN);
105      * def.addDatasource("output", "COUNTER", 600, 0, Double.NaN);
106      * def.addArchive("AVERAGE", 0.5, 1, 600);
107      * def.addArchive("AVERAGE", 0.5, 6, 700);
108      * def.addArchive("AVERAGE", 0.5, 24, 797);
109      * def.addArchive("AVERAGE", 0.5, 288, 775);
110      * def.addArchive("MAX", 0.5, 1, 600);
111      * def.addArchive("MAX", 0.5, 6, 700);
112      * def.addArchive("MAX", 0.5, 24, 797);
113      * def.addArchive("MAX", 0.5, 288, 775);
114      *
115      * // RRD definition is now completed, create the database!
116      * RrdDb rrd = new RrdDb(def);
117      * // new RRD file has been created on your disk
118      * </pre>
119      *
120      * @param rrdDef Object describing the structure of the new RRD file.
121      * @throws IOException Thrown in case of I/O error.
122      * @throws RrdException Thrown if invalid RrdDef object is supplied.
123      */

124     public RrdDb(RrdDef rrdDef) throws RrdException, IOException JavaDoc {
125         this(rrdDef, RrdFileBackendFactory.getDefaultFactory());
126     }
127
128     /**
129      * <p>Constructor used to create new RRD object from the definition object but with a storage
130      * (backend) different from default.</p>
131      *
132      * <p>JRobin uses <i>factories</i> to create RRD backend objecs. There are three different
133      * backend factories supplied with JRobin, and each factory has its unique name:</p>
134      *
135      * <ul>
136      * <li><b>FILE</b>: backends created from this factory will store RRD data to files by using
137      * java.io.* classes and methods
138      * <li><b>NIO</b>: backends created from this factory will store RRD data to files by using
139      * java.nio.* classes and methods
140      * <li><b>MEMORY</b>: backends created from this factory will store RRD data in memory. This might
141      * be useful in runtime environments which prohibit disk utilization, or for storing temporary,
142      * non-critical data (it gets lost as soon as JVM exits).
143      * </ul>
144      *
145      * <p>For example, to create RRD in memory, use the following code</p>
146      * <pre>
147      * RrdBackendFactory factory = RrdBackendFactory.getFactory("MEMORY");
148      * RrdDb rrdDb = new RrdDb(rrdDef, factory);
149      * rrdDb.close();
150      * </pre>
151      *
152      * <p>New RRD file structure is specified with an object of class
153      * {@link org.jrobin.core.RrdDef <b>RrdDef</b>}. The underlying RRD storage is created as soon
154      * as the constructor returns.</p>
155      * @param rrdDef RRD definition object
156      * @param factory The factory which will be used to create storage for this RRD
157      * @throws RrdException Thrown if invalid factory or definition is supplied
158      * @throws IOException Thrown in case of I/O error
159      * @see RrdBackendFactory
160      */

161     public RrdDb(RrdDef rrdDef, RrdBackendFactory factory) throws RrdException, IOException JavaDoc {
162         rrdDef.validate();
163         String JavaDoc path = rrdDef.getPath();
164         backend = factory.open(path, false, lockMode);
165         backend.setLength(rrdDef.getEstimatedSize());
166         // create header
167
header = new Header(this, rrdDef);
168         // create datasources
169
DsDef[] dsDefs = rrdDef.getDsDefs();
170         datasources = new Datasource[dsDefs.length];
171         for(int i = 0; i < dsDefs.length; i++) {
172             datasources[i] = new Datasource(this, dsDefs[i]);
173         }
174         // create archives
175
ArcDef[] arcDefs = rrdDef.getArcDefs();
176         archives = new Archive[arcDefs.length];
177         for(int i = 0; i < arcDefs.length; i++) {
178             archives[i] = new Archive(this, arcDefs[i]);
179         }
180         backend.afterCreate();
181     }
182
183     /**
184      * <p>Constructor used to open already existing RRD. This RRD object will be backed
185      * with a storage (backend) of the default type (file on the disk). Constructor
186      * obtains read or read/write access to this RRD.</p>
187      *
188      * @param path Path to existing RRD.
189      * @param readOnly Should be set to <code>false</code> if you want to update
190      * the underlying RRD. If you want just to fetch data from the RRD file
191      * (read-only access), specify <code>true</code>. If you try to update RRD file
192      * open in read-only mode (<code>readOnly</code> set to <code>true</code>),
193      * <code>IOException</code> will be thrown.
194      * @throws IOException Thrown in case of I/O error.
195      * @throws RrdException Thrown in case of JRobin specific error.
196      */

197     public RrdDb(String JavaDoc path, boolean readOnly) throws IOException JavaDoc, RrdException {
198         this(path, readOnly, RrdBackendFactory.getDefaultFactory());
199     }
200
201     /**
202      * <p>Constructor used to open already existing RRD backed
203      * with a storage (backend) different from default. Constructor
204      * obtains read or read/write access to this RRD.</p>
205      *
206      * @param path Path to existing RRD.
207      * @param readOnly Should be set to <code>false</code> if you want to update
208      * the underlying RRD. If you want just to fetch data from the RRD file
209      * (read-only access), specify <code>true</code>. If you try to update RRD file
210      * open in read-only mode (<code>readOnly</code> set to <code>true</code>),
211      * <code>IOException</code> will be thrown.
212      * @param factory Backend factory which will be used for this RRD.
213      * @throws IOException Thrown in case of I/O error.
214      * @throws RrdException Thrown in case of JRobin specific error.
215      * @see RrdBackendFactory
216      */

217     public RrdDb(String JavaDoc path, boolean readOnly, RrdBackendFactory factory)
218             throws IOException JavaDoc, RrdException {
219         // opens existing RRD file - throw exception if the file does not exist...
220
if(!factory.exists(path)) {
221             throw new IOException JavaDoc("Could not open " + path + " [non existent]");
222         }
223         backend = factory.open(path, readOnly, lockMode);
224         // restore header
225
header = new Header(this, (RrdDef) null);
226         header.validateHeader();
227         // restore datasources
228
int dsCount = header.getDsCount();
229         datasources = new Datasource[dsCount];
230         for(int i = 0; i < dsCount; i++) {
231             datasources[i] = new Datasource(this, null);
232         }
233         // restore archives
234
int arcCount = header.getArcCount();
235         archives = new Archive[arcCount];
236         for(int i = 0; i < arcCount; i++) {
237             archives[i] = new Archive(this, null);
238         }
239     }
240
241     /**
242      * <p>Constructor used to open already existing RRD in R/W mode, with a default storage
243      * (backend) type (file on the disk).
244      *
245      * @param path Path to existing RRD.
246      * @throws IOException Thrown in case of I/O error.
247      * @throws RrdException Thrown in case of JRobin specific error.
248      */

249     public RrdDb(String JavaDoc path) throws IOException JavaDoc, RrdException {
250         this(path, false);
251     }
252
253     /**
254      * <p>Constructor used to open already existing RRD in R/W mode with a storage (backend) type
255      * different from default.</p>
256      *
257      * @param path Path to existing RRD.
258      * @param factory Backend factory used to create this RRD.
259      * @throws IOException Thrown in case of I/O error.
260      * @throws RrdException Thrown in case of JRobin specific error.
261      * @see RrdBackendFactory
262      */

263     public RrdDb(String JavaDoc path, RrdBackendFactory factory) throws IOException JavaDoc, RrdException {
264         this(path, false, factory);
265     }
266
267     /**
268      * <p>Constructor used to create new RRD from XML dump. Newly created RRD will be backed
269      * with a default storage (backend) type (file on the disk). JRobin and RRDTool
270      * use the same format for XML dump and this constructor should be used to
271      * (re)create JRobin RRD from XML. In other words, it is possible to convert
272      * RRDTool RRD files to JRobin RRD files: first, dump the content of RRDTool
273      * RRD file (use command line):</p>
274      *
275      * <code>rrdtool dump original.rrd > original.xml</code>
276      *
277      * <p>Than, use file <code>original.xml</code> to create JRobin RRD file
278      * <code>copy.rrd</code>:</p>
279      *
280      * <code>RrdDb rrd = new RrdDb("copy.rrd", "original.xml");</code>
281      *
282      * <p>See documentation for {@link #dumpXml(java.lang.String) dumpXml()} method
283      * how to convert JRobin files to RRDTool format.</p>
284      *
285      * @param rrdPath Path to RRD file which will be created
286      * @param xmlPath Path to file containing XML dump of RRDTool's or JRobin's RRD file
287      * @throws IOException Thrown in case of I/O error
288      * @throws RrdException Thrown in case of JRobin specific error
289      */

290     public RrdDb(String JavaDoc rrdPath, String JavaDoc xmlPath) throws IOException JavaDoc, RrdException {
291         this(rrdPath, xmlPath, RrdBackendFactory.getDefaultFactory());
292     }
293
294     /**
295      * <p>Constructor used to create new RRD from XML dump but with a storage (backend) type
296      * different from default.</p>
297      *
298      * @param rrdPath Path to RRD which will be created
299      * @param xmlPath Path to file containing XML dump of RRDTool's or JRobin's RRD file
300      * @param factory Backend factory which will be used to create storage (backend) for this RRD.
301      * @throws IOException Thrown in case of I/O error
302      * @throws RrdException Thrown in case of JRobin specific error
303      * @see RrdBackendFactory
304      */

305     public RrdDb(String JavaDoc rrdPath, String JavaDoc xmlPath, RrdBackendFactory factory)
306             throws IOException JavaDoc, RrdException {
307         backend = factory.open(rrdPath, false, lockMode);
308         DataImporter reader;
309         if(xmlPath.startsWith("rrdtool:/")) {
310             String JavaDoc rrdToolPath = xmlPath.substring("rrdtool:/".length());
311             reader = new RrdToolReader(rrdToolPath);
312         }
313         else if(xmlPath.startsWith("xml:/")) {
314             xmlPath = xmlPath.substring("xml:/".length());
315             reader = new XmlReader(xmlPath);
316         }
317         else {
318             reader = new XmlReader(xmlPath);
319         }
320         backend.setLength(reader.getEstimatedSize());
321         // create header
322
header = new Header(this, reader);
323         // create datasources
324
datasources = new Datasource[reader.getDsCount()];
325         for(int i = 0; i < datasources.length; i++) {
326             datasources[i] = new Datasource(this, reader, i);
327         }
328         // create archives
329
archives = new Archive[reader.getArcCount()];
330         for(int i = 0; i < archives.length; i++) {
331             archives[i] = new Archive(this, reader, i);
332         }
333         reader.release();
334         // XMLReader is a rather huge DOM tree, release memory ASAP
335
reader = null;
336         backend.afterCreate();
337     }
338
339     /**
340      * Closes RRD. No further operations are allowed on this RrdDb object.
341      *
342      * @throws IOException Thrown in case of I/O related error.
343      */

344     public synchronized void close() throws IOException JavaDoc {
345         if(!closed) {
346             backend.close();
347             closed = true;
348         }
349     }
350
351     /**
352      * Returns true if the RRD is closed.
353      * @return true if closed, false otherwise
354      */

355     public boolean isClosed() {
356         return closed;
357     }
358
359     /**
360      * Returns RRD header.
361      * @return Header object
362      */

363     public Header getHeader() {
364         return header;
365     }
366
367     /**
368      * Returns Datasource object for the given datasource index.
369      * @param dsIndex Datasource index (zero based)
370      * @return Datasource object
371      */

372     public Datasource getDatasource(int dsIndex) {
373         return datasources[dsIndex];
374     }
375
376     /**
377      * Returns Archive object for the given archive index.
378      * @param arcIndex Archive index (zero based)
379      * @return Archive object
380      */

381     public Archive getArchive(int arcIndex) {
382         return archives[arcIndex];
383     }
384
385     /**
386      * <p>Returns an array of datasource names defined in RRD.</p>
387      *
388      * @return Array of datasource names.
389      * @throws IOException Thrown in case of I/O error.
390      */

391     public String JavaDoc[] getDsNames() throws IOException JavaDoc {
392         int n = datasources.length;
393         String JavaDoc[] dsNames = new String JavaDoc[n];
394         for(int i = 0; i < n; i++) {
395             dsNames[i] = datasources[i].getDsName();
396         }
397         return dsNames;
398     }
399
400     /**
401      * <p>Creates new sample with the given timestamp and all datasource values set to
402      * 'unknown'. Use returned <code>Sample</code> object to specify
403      * datasource values for the given timestamp. See documentation for
404      * {@link org.jrobin.core.Sample Sample} for an explanation how to do this.</p>
405      *
406      * <p>Once populated with data source values, call Sample's
407      * {@link org.jrobin.core.Sample#update() update()} method to actually
408      * store sample in the RRD associated with it.</p>
409      * @param time Sample timestamp rounded to the nearest second (without milliseconds).
410      * @return Fresh sample with the given timestamp and all data source values set to 'unknown'.
411      * @throws IOException Thrown in case of I/O error.
412      */

413     public Sample createSample(long time) throws IOException JavaDoc {
414         return new Sample(this, time);
415     }
416
417     /**
418      * <p>Creates new sample with the current timestamp and all data source values set to
419      * 'unknown'. Use returned <code>Sample</code> object to specify
420      * datasource values for the current timestamp. See documentation for
421      * {@link org.jrobin.core.Sample Sample} for an explanation how to do this.</p>
422      *
423      * <p>Once populated with data source values, call Sample's
424      * {@link org.jrobin.core.Sample#update() update()} method to actually
425      * store sample in the RRD associated with it.</p>
426      * @return Fresh sample with the current timestamp and all
427      * data source values set to 'unknown'.
428      * @throws IOException Thrown in case of I/O error.
429      */

430     public Sample createSample() throws IOException JavaDoc {
431         return createSample(Util.getTime());
432     }
433
434     /**
435      * <p>Prepares fetch request to be executed on this RRD. Use returned
436      * <code>FetchRequest</code> object and its {@link org.jrobin.core.FetchRequest#fetch() fetch()}
437      * method to actually fetch data from the RRD file.</p>
438      * @param consolFun Consolidation function to be used in fetch request. Allowed values are
439      * "AVERAGE", "MIN", "MAX" and "LAST".
440      * @param fetchStart Starting timestamp for fetch request.
441      * @param fetchEnd Ending timestamp for fetch request.
442      * @param resolution Fetch resolution (see RRDTool's
443      * <a HREF="../../../../man/rrdfetch.html" target="man">rrdfetch man page</a> for an
444      * explanation of this parameter.
445      * @return Request object that should be used to actually fetch data from RRD.
446      * @throws RrdException In case of JRobin related error (invalid consolidation function or
447      * invalid time span).
448      */

449     public FetchRequest createFetchRequest(String JavaDoc consolFun, long fetchStart, long fetchEnd,
450         long resolution) throws RrdException {
451         return new FetchRequest(this, consolFun, fetchStart, fetchEnd, resolution);
452     }
453
454     /**
455      * <p>Prepares fetch request to be executed on this RRD. Use returned
456      * <code>FetchRequest</code> object and its {@link org.jrobin.core.FetchRequest#fetch() fetch()}
457      * method to actually fetch data from this RRD. Data will be fetched with the smallest
458      * possible resolution (see RRDTool's
459      * <a HREF="../../../../man/rrdfetch.html" target="man">rrdfetch man page</a>
460      * for the explanation of the resolution parameter).</p>
461      *
462      * @param consolFun Consolidation function to be used in fetch request. Allowed values are
463      * "AVERAGE", "MIN", "MAX" and "LAST".
464      * @param fetchStart Starting timestamp for fetch request.
465      * @param fetchEnd Ending timestamp for fetch request.
466      * @return Request object that should be used to actually fetch data from RRD.
467      * @throws RrdException In case of JRobin related error (invalid consolidation function or
468      * invalid time span).
469      */

470     public FetchRequest createFetchRequest(String JavaDoc consolFun, long fetchStart, long fetchEnd)
471         throws RrdException {
472         return createFetchRequest(consolFun, fetchStart, fetchEnd, 1);
473     }
474
475     synchronized void store(Sample sample) throws IOException JavaDoc, RrdException {
476         if(closed) {
477             throw new RrdException("RRD already closed, cannot store this sample");
478         }
479         backend.beforeUpdate();
480         long newTime = sample.getTime();
481         long lastTime = header.getLastUpdateTime();
482         if(lastTime >= newTime) {
483             throw new RrdException("Bad sample timestamp " + newTime +
484                 ". Last update time was " + lastTime + ", at least one second step is required");
485         }
486         double[] newValues = sample.getValues();
487         for(int i = 0; i < datasources.length; i++) {
488             double newValue = newValues[i];
489             datasources[i].process(newTime, newValue);
490         }
491         header.setLastUpdateTime(newTime);
492         backend.afterUpdate();
493     }
494
495     synchronized FetchPoint[] fetch(FetchRequest request) throws IOException JavaDoc, RrdException {
496         if(closed) {
497             throw new RrdException("RRD already closed, cannot fetch data");
498         }
499         backend.beforeFetch();
500         Archive archive = findMatchingArchive(request);
501         FetchPoint[] points = archive.fetch(request);
502         backend.afterFetch();
503         return points;
504     }
505
506     synchronized FetchData fetchData(FetchRequest request) throws IOException JavaDoc, RrdException {
507         if(closed) {
508             throw new RrdException("RRD already closed, cannot fetch data");
509         }
510         backend.beforeFetch();
511         Archive archive = findMatchingArchive(request);
512         FetchData fetchData = archive.fetchData(request);
513         backend.afterFetch();
514         return fetchData;
515     }
516
517     public Archive findMatchingArchive(FetchRequest request) throws RrdException, IOException JavaDoc
518     {
519         String JavaDoc consolFun = request.getConsolFun();
520         long fetchStart = request.getFetchStart();
521         long fetchEnd = request.getFetchEnd();
522         long resolution = request.getResolution();
523
524         Archive bestFullMatch = null, bestPartialMatch = null;
525
526         long bestStepDiff = 0, bestMatch = 0;
527
528         for ( int i = 0; i < archives.length; i++ )
529         {
530             if ( archives[i].getConsolFun().equals(consolFun) )
531             {
532                 long arcStep = archives[i].getArcStep();
533                 long arcStart = archives[i].getStartTime() - arcStep;
534                 long arcEnd = archives[i].getEndTime();
535                 long fullMatch = fetchEnd - fetchStart;
536
537                 if ( arcEnd >= fetchEnd && arcStart <= fetchStart ) // best full match
538
{
539                     long tmpStepDiff = Math.abs( archives[i].getArcStep() - resolution );
540
541                     if ( tmpStepDiff < bestStepDiff || bestFullMatch == null )
542                     {
543                         bestStepDiff = tmpStepDiff;
544                         bestFullMatch = archives[i];
545                     }
546
547                 }
548                 else // best partial match
549
{
550                     long tmpMatch = fullMatch;
551                     
552                     if ( arcStart > fetchStart )
553                         tmpMatch -= (arcStart - fetchStart);
554                     if( arcEnd < fetchEnd )
555                         tmpMatch -= (fetchEnd - arcEnd);
556
557                     if ( bestPartialMatch == null || bestMatch < tmpMatch )
558                     {
559                         bestPartialMatch = archives[i];
560                         bestMatch = tmpMatch;
561                     }
562                 }
563             }
564         }
565
566         if ( bestFullMatch != null )
567             return bestFullMatch;
568         else if ( bestPartialMatch != null )
569             return bestPartialMatch;
570         else
571             throw new RrdException("RRD file does not contain RRA:" + consolFun + " archive");
572     }
573
574     /**
575      * Finds the archive that best matches to the start time (time period being start-time until now)
576      * and requested resolution.
577      * @param consolFun Consolidation function of the datasource.
578      * @param startTime Start time of the time period in seconds.
579      * @param resolution Requested fetch resolution.
580      * @return Reference to the best matching archive.
581      * @throws IOException Thrown in case of I/O related error.
582      */

583     public Archive findStartMatchArchive( String JavaDoc consolFun, long startTime, long resolution ) throws IOException JavaDoc
584     {
585         long arcStep, diff;
586         int fallBackIndex = 0;
587         int arcIndex = -1;
588         long minDiff = Long.MAX_VALUE;
589         long fallBackDiff = Long.MAX_VALUE;
590
591         for ( int i = 0; i < archives.length; i++ )
592         {
593             if ( archives[i].getConsolFun().equals(consolFun) )
594             {
595                 arcStep = archives[i].getArcStep();
596                 diff = Math.abs( resolution - arcStep );
597
598                 // Now compare start time, see if this archive encompasses the requested interval
599
if ( startTime >= archives[i].getStartTime() )
600                 {
601                     if ( diff == 0 ) // Best possible match either way
602
return archives[i];
603                     else if ( diff < minDiff ) {
604                         minDiff = diff;
605                         arcIndex = i;
606                     }
607                 }
608                 else if ( diff < fallBackDiff ) {
609                     fallBackDiff = diff;
610                     fallBackIndex = i;
611                 }
612             }
613         }
614
615         return ( arcIndex >= 0 ? archives[arcIndex] : archives[fallBackIndex] );
616     }
617
618     /**
619      * <p>Returns string representing complete internal RRD state. The returned
620      * string can be printed to <code>stdout</code> and/or used for debugging purposes.</p>
621      * @return String representing internal RRD state.
622      * @throws IOException Thrown in case of I/O related error.
623      */

624     public synchronized String JavaDoc dump() throws IOException JavaDoc {
625         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
626         buffer.append(header.dump());
627         for(int i = 0; i < datasources.length; i++) {
628             buffer.append(datasources[i].dump());
629         }
630         for(int i = 0; i < archives.length; i++) {
631             buffer.append(archives[i].dump());
632         }
633         return buffer.toString();
634     }
635
636     void archive(Datasource datasource, double value, long numUpdates)
637         throws IOException JavaDoc, RrdException {
638         int dsIndex = getDsIndex(datasource.getDsName());
639         for(int i = 0; i < archives.length; i++) {
640             archives[i].archive(dsIndex, value, numUpdates);
641         }
642     }
643
644     /**
645      * <p>Returns internal index number for the given datasource name. This index is heavily
646      * used by jrobin.graph package and has no value outside of it.</p>
647      * @param dsName Data source name.
648      * @return Internal index of the given data source name in this RRD.
649      * @throws RrdException Thrown in case of JRobin related error (invalid data source name,
650      * for example)
651      * @throws IOException Thrown in case of I/O error.
652      */

653     public int getDsIndex(String JavaDoc dsName) throws RrdException, IOException JavaDoc {
654         for(int i = 0; i < datasources.length; i++) {
655             if(datasources[i].getDsName().equals(dsName)) {
656                 return i;
657             }
658         }
659         throw new RrdException("Unknown datasource name: " + dsName);
660     }
661
662     Datasource[] getDatasources() {
663         return datasources;
664     }
665
666     Archive[] getArchives() {
667         return archives;
668     }
669
670     /**
671      * <p>Writes the RRD content to OutputStream using XML format. This format
672      * is fully compatible with RRDTool's XML dump format and can be used for conversion
673      * purposes or debugging.</p>
674      * @param destination Output stream to receive XML data
675      * @throws IOException Thrown in case of I/O related error
676      */

677     public synchronized void dumpXml(OutputStream JavaDoc destination) throws IOException JavaDoc {
678         XmlWriter writer = new XmlWriter(destination);
679         writer.startTag("rrd");
680         // dump header
681
header.appendXml(writer);
682         // dump datasources
683
for(int i = 0; i < datasources.length; i++) {
684             datasources[i].appendXml(writer);
685         }
686         // dump archives
687
for(int i = 0; i < archives.length; i++) {
688             archives[i].appendXml(writer);
689         }
690         writer.closeTag();
691         writer.flush();
692     }
693
694     /**
695      * This method is just an alias for {@link #dumpXml(OutputStream) dumpXml} method.
696      * @throws IOException Thrown in case of I/O related error
697      */

698     public synchronized void exportXml(OutputStream JavaDoc destination) throws IOException JavaDoc {
699         dumpXml(destination);
700     }
701
702     /**
703      * <p>Returns string representing internal RRD state in XML format. This format
704      * is fully compatible with RRDTool's XML dump format and can be used for conversion
705      * purposes or debugging.</p>
706      * @return Internal RRD state in XML format.
707      * @throws IOException Thrown in case of I/O related error
708      * @throws RrdException Thrown in case of JRobin specific error
709      */

710     public synchronized String JavaDoc getXml() throws IOException JavaDoc, RrdException {
711         ByteArrayOutputStream JavaDoc destination = new ByteArrayOutputStream JavaDoc(XML_INITIAL_BUFFER_CAPACITY);
712         dumpXml(destination);
713         return destination.toString();
714     }
715
716     /**
717      * This method is just an alias for {@link #getXml() getXml} method.
718      * @return Internal RRD state in XML format.
719      * @throws IOException Thrown in case of I/O related error
720      * @throws RrdException Thrown in case of JRobin specific error
721      */

722     public synchronized String JavaDoc exportXml() throws IOException JavaDoc, RrdException {
723         return getXml();
724     }
725
726     /**
727      * <p>Dumps internal RRD state to XML file.
728      * Use this XML file to convert your JRobin RRD to RRDTool format.</p>
729      *
730      * <p>Suppose that you have a JRobin RRD file <code>original.rrd</code> and you want
731      * to convert it to RRDTool format. First, execute the following java code:</p>
732      *
733      * <code>RrdDb rrd = new RrdDb("original.rrd");
734      * rrd.dumpXml("original.xml");</code>
735      *
736      * <p>Use <code>original.xml</code> file to create the corresponding RRDTool file
737      * (from your command line):
738      *
739      * <code>rrdtool restore copy.rrd original.xml</code>
740      *
741      * @param filename Path to XML file which will be created.
742      * @throws IOException Thrown in case of I/O related error.
743      * @throws RrdException Thrown in case of JRobin related error.
744      */

745     public synchronized void dumpXml(String JavaDoc filename) throws IOException JavaDoc, RrdException {
746         OutputStream JavaDoc outputStream = null;
747         try {
748             outputStream = new FileOutputStream JavaDoc(filename, false);
749             dumpXml(outputStream);
750         }
751         finally {
752             if(outputStream != null) {
753                 outputStream.close();
754             }
755         }
756     }
757
758     /**
759      * This method is just an alias for {@link #dumpXml(String) dumpXml(String)} method.
760      * @throws IOException Thrown in case of I/O related error
761      * @throws RrdException Thrown in case of JRobin specific error
762      */

763     public synchronized void exportXml(String JavaDoc filename) throws IOException JavaDoc, RrdException {
764         dumpXml(filename);
765     }
766
767     /**
768      * Returns time of last update operation as timestamp (in seconds).
769      * @return Last update time (in seconds).
770      */

771     public synchronized long getLastUpdateTime() throws IOException JavaDoc {
772         return header.getLastUpdateTime();
773     }
774
775     /**
776      * <p>Returns RRD definition object which can be used to create new RRD
777      * with the same creation parameters but with no data in it.</p>
778      *
779      * <p>Example:</p>
780      *
781      * <pre>
782      * RrdDb rrd1 = new RrdDb("original.rrd");
783      * RrdDef def = rrd1.getRrdDef();
784      * // fix path
785      * def.setPath("empty_copy.rrd");
786      * // create new RRD file
787      * RrdDb rrd2 = new RrdDb(def);
788      * </pre>
789      * @return RRD definition.
790      * @throws RrdException Thrown in case of JRobin specific error.
791      */

792     public synchronized RrdDef getRrdDef() throws RrdException, IOException JavaDoc {
793         // set header
794
long startTime = header.getLastUpdateTime();
795         long step = header.getStep();
796         String JavaDoc path = backend.getPath();
797         RrdDef rrdDef = new RrdDef(path, startTime, step);
798         // add datasources
799
for(int i = 0; i < datasources.length; i++) {
800             DsDef dsDef = new DsDef(datasources[i].getDsName(),
801                 datasources[i].getDsType(), datasources[i].getHeartbeat(),
802                 datasources[i].getMinValue(), datasources[i].getMaxValue());
803             rrdDef.addDatasource(dsDef);
804         }
805         // add archives
806
for(int i = 0; i < archives.length; i++) {
807             ArcDef arcDef = new ArcDef(archives[i].getConsolFun(),
808                 archives[i].getXff(), archives[i].getSteps(), archives[i].getRows());
809             rrdDef.addArchive(arcDef);
810         }
811         return rrdDef;
812     }
813
814     /**
815      * <p>Returns current lock mode. This function can return one of the following values:
816      * <ul>
817      * <li><code>NO_LOCKS</code>: RRD files are not locked (default). Simultaneous access
818      * to the same RRD file is allowed. This locking mode provides fastest read/write
819      * (fetch/update) operations, but could lead to inconsisten RRD data.</li>
820      * <li><code>WAIT_IF_LOCKED</code>: RRD files are locked exclusively.
821      * Simultaneous access to the same underlying RRD file is not allowed.
822      * If a <code>RrdDb</code> object tries to access already locked RRD file,
823      * it will wait until the lock is released. The lock is released when
824      * the {@link #close() close()} method is called.</li>
825      * <li><code>EXCEPTION_IF_LOCKED</code>: RRD files are locked exclusively.
826      * Simultaneous access to the same underlying RRD file is not allowed.
827      * If a <code>RrdDb</code> object tries to access already locked RRD file,
828      * an exception is thrown.</li></p>
829      * </ul>
830      * <p>Note that <code>WAIT_IF_LOCKED</code> and <code>EXCEPTION_IF_LOCKED</code>
831      * modes guarantee data consistency but affect the speed of read/write operations.
832      * Call {@link #close() close()} as soon as possible in your code to avoid long wait states
833      * (or locking exceptions). </p>
834      * @return The current locking behaviour.
835      */

836     public static int getLockMode() {
837         return RrdDb.lockMode;
838     }
839
840     /**
841      * Sets the current locking mode. See {@link #getLockMode() getLockMode()} for more
842      * information.
843      * @param lockMode Lock mode. Valid values are <code>NO_LOCKS</code>,
844      * <code>WAIT_IF_LOCKED</code> and <code>EXCEPTION_IF_LOCKED</code>.
845      */

846     public static void setLockMode(int lockMode) {
847         RrdDb.lockMode = lockMode;
848     }
849
850     protected void finalize() throws Throwable JavaDoc {
851         close();
852     }
853
854     /**
855      * Copies object's internal state to another RrdDb object.
856      * @param other New RrdDb object to copy state to
857      * @throws IOException Thrown in case of I/O error
858      * @throws RrdException Thrown if supplied argument is not a compatible RrdDb object
859      */

860     public synchronized void copyStateTo(RrdUpdater other) throws IOException JavaDoc, RrdException {
861         if(!(other instanceof RrdDb)) {
862             throw new RrdException(
863                 "Cannot copy RrdDb object to " + other.getClass().getName());
864         }
865         RrdDb otherRrd = (RrdDb) other;
866         header.copyStateTo(otherRrd.header);
867         for(int i = 0; i < datasources.length; i++) {
868             int j = Util.getMatchingDatasourceIndex(this, i, otherRrd);
869             if(j >= 0) {
870                 datasources[i].copyStateTo(otherRrd.datasources[j]);
871             }
872         }
873         for(int i = 0; i < archives.length; i++) {
874             int j = Util.getMatchingArchiveIndex(this, i, otherRrd);
875             if(j >= 0) {
876                 archives[i].copyStateTo(otherRrd.archives[j]);
877             }
878         }
879     }
880
881     /**
882      * Returns Datasource object corresponding to the given datasource name.
883      * @param dsName Datasource name
884      * @return Datasource object corresponding to the give datasource name or null
885      * if not found.
886      * @throws IOException Thrown in case of I/O error
887      */

888     public Datasource getDatasource(String JavaDoc dsName) throws IOException JavaDoc {
889         try {
890             return getDatasource(getDsIndex(dsName));
891         }
892         catch (RrdException e) {
893             return null;
894         }
895     }
896
897     /**
898      * Returns index of Archive object with the given consolidation function and the number
899      * of steps. Exception is thrown if such archive could not be found.
900      * @param consolFun Consolidation function
901      * @param steps Number of archive steps
902      * @return Requested Archive object
903      * @throws IOException Thrown in case of I/O error
904      * @throws RrdException Thrown if no such archive could be found
905      */

906     public int getArcIndex(String JavaDoc consolFun, int steps) throws RrdException, IOException JavaDoc {
907         for(int i = 0; i < archives.length; i++) {
908             if(archives[i].getConsolFun().equals(consolFun) &&
909                 archives[i].getSteps() == steps) {
910                 return i;
911             }
912         }
913         throw new RrdException("Could not find archive " + consolFun + "/" + steps);
914     }
915
916     /**
917      * Returns Archive object with the given consolidation function and the number
918      * of steps.
919      * @param consolFun Consolidation function
920      * @param steps Number of archive steps
921      * @return Requested Archive object or null if no such archive could be found
922      * @throws IOException Thrown in case of I/O error
923      */

924     public Archive getArchive(String JavaDoc consolFun, int steps) throws IOException JavaDoc {
925         try {
926             return getArchive(getArcIndex(consolFun, steps));
927         }
928         catch (RrdException e) {
929             return null;
930         }
931     }
932
933     /**
934      * Returns canonical path to the underlying RRD file. Note that this method makes sense just for
935      * ordinary RRD files created on the disk - an exception will be thrown for RRD objects created in
936      * memory or with custom backends.
937      * @return Canonical path to RRD file;
938      * @throws IOException Thrown in case of I/O error or if the underlying backend is
939      * not derived from RrdFileBackend.
940      */

941     public String JavaDoc getCanonicalPath() throws IOException JavaDoc {
942         if(backend instanceof RrdFileBackend) {
943             return ((RrdFileBackend) backend).getCanonicalPath();
944         }
945         else {
946             throw new IOException JavaDoc("The underlying backend has no canonical path");
947         }
948     }
949
950     /**
951      * Returns path to this RRD.
952      * @return Path to this RRD.
953      */

954     public String JavaDoc getPath() {
955         return backend.getPath();
956     }
957
958     /**
959      * Returns backend object for this RRD which performs actual I/O operations.
960      * @return RRD backend for this RRD.
961      */

962     public RrdBackend getRrdBackend() {
963         return backend;
964     }
965
966     /**
967      * Required to implement RrdUpdater interface. You should never call this method directly.
968      * @return Allocator object
969      */

970     public RrdAllocator getRrdAllocator() {
971         return allocator;
972     }
973
974     /**
975      * Returns an array of bytes representing the whole RRD.
976      * @return All RRD bytes
977      * @throws IOException Thrown in case of I/O related error.
978      */

979     public synchronized byte[] getBytes() throws IOException JavaDoc {
980         return backend.readAll();
981     }
982
983     /**
984      * This method forces all RRD data cached in memory but not yet stored in the persistant
985      * storage, to be stored in it. This method should be used only if you RrdDb object uses
986      * RrdBackend which implements any kind of data caching (like {@link RrdNioBackend}). This method
987      * need not be called before the {@link #close()} method call since the close() method always
988      * synchronizes all data in memory with the data in the persisant storage.<p>
989      *
990      * When this method returns it is guaranteed that RRD data in the persistent storage is
991      * synchronized with the RrdDb data in memory.<p>
992      *
993      * @throws IOException Thrown in case of I/O error
994      */

995     public synchronized void sync() throws IOException JavaDoc {
996         backend.sync();
997     }
998
999     /**
1000     * Sets default backend factory to be used. This method is just an alias for
1001     * {@link RrdBackendFactory#setDefaultFactory(java.lang.String)}.<p>
1002     * @param factoryName Name of the backend factory to be set as default.
1003     * @throws RrdException Thrown if invalid factory name is supplied, or not called
1004     * before the first backend object (before the first RrdDb object) is created.
1005     */

1006    public static void setDefaultFactory(String JavaDoc factoryName) throws RrdException {
1007        RrdBackendFactory.setDefaultFactory(factoryName);
1008    }
1009
1010    public static void main(String JavaDoc[] args) {
1011        System.out.println("JRobin base directory: " + Util.getJRobinHomeDirectory());
1012        System.out.println("JRobin Java Library :: RRDTool choice for the Java world");
1013        System.out.println("http://www.jrobin.org");
1014        System.out.println("(C) 2004 Sasa Markovic & Arne Vandamme");
1015    }
1016
1017}
1018
Popular Tags