001    /* ============================================================
002     * JRobin : Pure java implementation of RRDTool's functionality
003     * ============================================================
004     *
005     * Project Info:  http://www.jrobin.org
006     * Project Lead:  Sasa Markovic (saxon@jrobin.org);
007     *
008     * (C) Copyright 2003-2005, by Sasa Markovic.
009     *
010     * Developers:    Sasa Markovic (saxon@jrobin.org)
011     *
012     *
013     * This library is free software; you can redistribute it and/or modify it under the terms
014     * of the GNU Lesser General Public License as published by the Free Software Foundation;
015     * either version 2.1 of the License, or (at your option) any later version.
016     *
017     * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
018     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
019     * See the GNU Lesser General Public License for more details.
020     *
021     * You should have received a copy of the GNU Lesser General Public License along with this
022     * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
023     * Boston, MA 02111-1307, USA.
024     */
025    
026    package org.jrobin.core;
027    
028    import java.io.IOException;
029    import java.util.HashMap;
030    
031    /**
032     * This class should be used to synchronize access to RRD files
033     * in a multithreaded environment. This class should be also used to prevent openning of
034     * too many RRD files at the same time (thus avoiding operating system limits)
035     */
036    
037    public class RrdDbPool {
038            /**
039             * Initial capacity of the pool i.e. maximum number of simultaneously open RRD files. The pool will
040             * never open too many RRD files at the same time.
041             */
042            public static final int INITIAL_CAPACITY = 200;
043            private static RrdDbPool instance;
044    
045            private int capacity = INITIAL_CAPACITY;
046            private HashMap<String, RrdEntry> rrdMap = new HashMap<String, RrdEntry>(INITIAL_CAPACITY);
047    
048            /**
049             * Creates a single instance of the class on the first call, or returns already existing one.
050             *
051             * @return Single instance of this class
052             * @throws RrdException Thrown if the default RRD backend is not derived from the {@link RrdFileBackendFactory}
053             */
054            public synchronized static RrdDbPool getInstance() throws RrdException {
055                    if (instance == null) {
056                            instance = new RrdDbPool();
057                    }
058                    return instance;
059            }
060    
061            private RrdDbPool() throws RrdException {
062                    RrdBackendFactory factory = RrdBackendFactory.getDefaultFactory();
063                    if (!(factory instanceof RrdFileBackendFactory)) {
064                            throw new RrdException("Cannot create instance of " + getClass().getName() + " with " +
065                                            "a default backend factory not derived from RrdFileBackendFactory");
066                    }
067            }
068    
069            /**
070             * Requests a RrdDb reference for the given RRD file path.<p>
071             * <ul>
072             * <li>If the file is already open, previously returned RrdDb reference will be returned. Its usage count
073             * will be incremented by one.
074             * <li>If the file is not already open and the number of already open RRD files is less than
075             * {@link #INITIAL_CAPACITY}, the file will be open and a new RrdDb reference will be returned.
076             * If the file is not already open and the number of already open RRD files is equal to
077             * {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
078             * </ul>
079             *
080             * @param path Path to existing RRD file
081             * @return reference for the give RRD file
082             * @throws IOException  Thrown in case of I/O error
083             * @throws RrdException Thrown in case of JRobin specific error
084             */
085            public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException {
086                    String canonicalPath = Util.getCanonicalPath(path);
087                    while (!rrdMap.containsKey(canonicalPath) && rrdMap.size() >= capacity) {
088                            try {
089                                    wait();
090                            }
091                            catch (InterruptedException e) {
092                                    throw new RrdException(e);
093                            }
094                    }
095                    if (rrdMap.containsKey(canonicalPath)) {
096                            // already open, just increase usage count
097                            RrdEntry entry = rrdMap.get(canonicalPath);
098                            entry.count++;
099                            return entry.rrdDb;
100                    }
101                    else {
102                            // not open, open it now and add to the map
103                            RrdDb rrdDb = new RrdDb(canonicalPath);
104                            rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
105                            return rrdDb;
106                    }
107            }
108    
109            /**
110             * Requests a RrdDb reference for the given RRD file definition object.<p>
111             * <ul>
112             * <li>If the file with the path specified in the RrdDef object is already open,
113             * the method blocks until the file is closed.
114             * <li>If the file is not already open and the number of already open RRD files is less than
115             * {@link #INITIAL_CAPACITY}, a new RRD file will be created and a its RrdDb reference will be returned.
116             * If the file is not already open and the number of already open RRD files is equal to
117             * {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
118             * </ul>
119             *
120             * @param rrdDef Definition of the RRD file to be created
121             * @return Reference to the newly created RRD file
122             * @throws IOException  Thrown in case of I/O error
123             * @throws RrdException Thrown in case of JRobin specific error
124             */
125            public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException {
126                    String canonicalPath = Util.getCanonicalPath(rrdDef.getPath());
127                    while (rrdMap.containsKey(canonicalPath) || rrdMap.size() >= capacity) {
128                            try {
129                                    wait();
130                            }
131                            catch (InterruptedException e) {
132                                    throw new RrdException(e);
133                            }
134                    }
135                    RrdDb rrdDb = new RrdDb(rrdDef);
136                    rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
137                    return rrdDb;
138            }
139    
140            /**
141             * Requests a RrdDb reference for the given path. The file will be created from
142             * external data (from XML dump, RRD file or RRDTool's binary RRD file).<p>
143             * <ul>
144             * <li>If the file with the path specified is already open,
145             * the method blocks until the file is closed.
146             * <li>If the file is not already open and the number of already open RRD files is less than
147             * {@link #INITIAL_CAPACITY}, a new RRD file will be created and a its RrdDb reference will be returned.
148             * If the file is not already open and the number of already open RRD files is equal to
149             * {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
150             * </ul>
151             *
152             * @param path     Path to RRD file which should be created
153             * @param sourcePath Path to external data which is to be converted to JRobin's native RRD file format
154             * @return Reference to the newly created RRD file
155             * @throws IOException  Thrown in case of I/O error
156             * @throws RrdException Thrown in case of JRobin specific error
157             */
158            public synchronized RrdDb requestRrdDb(String path, String sourcePath)
159                            throws IOException, RrdException {
160                    String canonicalPath = Util.getCanonicalPath(path);
161                    while (rrdMap.containsKey(canonicalPath) || rrdMap.size() >= capacity) {
162                            try {
163                                    wait();
164                            }
165                            catch (InterruptedException e) {
166                                    throw new RrdException(e);
167                            }
168                    }
169                    RrdDb rrdDb = new RrdDb(canonicalPath, sourcePath);
170                    rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
171                    return rrdDb;
172            }
173    
174            /**
175             * Releases RrdDb reference previously obtained from the pool. When a reference is released, its usage
176             * count is decremented by one. If usage count drops to zero, the underlying RRD file will be closed.
177             *
178             * @param rrdDb RrdDb reference to be returned to the pool
179             * @throws IOException  Thrown in case of I/O error
180             * @throws RrdException Thrown in case of JRobin specific error
181             */
182            public synchronized void release(RrdDb rrdDb) throws IOException, RrdException {
183                    // null pointer should not kill the thread, just ignore it
184                    if (rrdDb == null) {
185                            return;
186                    }
187                    String canonicalPath = Util.getCanonicalPath(rrdDb.getPath());
188                    if (!rrdMap.containsKey(canonicalPath)) {
189                            throw new RrdException("Could not release [" + canonicalPath + "], the file was never requested");
190                    }
191                    RrdEntry entry = rrdMap.get(canonicalPath);
192                    if (--entry.count <= 0) {
193                            // no longer used
194                            rrdMap.remove(canonicalPath);
195                            notifyAll();
196                            entry.rrdDb.close();
197                    }
198            }
199    
200            /**
201             * Returns the maximum number of simultaneously open RRD files.
202             *
203             * @return maximum number of simultaneously open RRD files
204             */
205            public synchronized int getCapacity() {
206                    return capacity;
207            }
208    
209            /**
210             * Sets the maximum number of simultaneously open RRD files.
211             *
212             * @param capacity Maximum number of simultaneously open RRD files.
213             */
214            public synchronized void setCapacity(int capacity) {
215                    this.capacity = capacity;
216            }
217    
218            /**
219             * Returns an array of open file names.
220             *
221             * @return Array with canonical paths to open RRD files held in the pool.
222             */
223            public synchronized String[] getOpenFiles() {
224                    return rrdMap.keySet().toArray(new String[0]);
225            }
226    
227            /**
228             * Returns the number of open RRD files.
229             *
230             * @return Number of currently open RRD files held in the pool.
231             */
232            public synchronized int getOpenFileCount() {
233                    return rrdMap.size();
234            }
235    
236            class RrdEntry {
237                    RrdDb rrdDb;
238                    int count;
239    
240                    RrdEntry(RrdDb rrdDb) {
241                            this.rrdDb = rrdDb;
242                            this.count = 1;
243                    }
244            }
245    }
246    
247    // OLDER VERSION IS HERE
248    
249    ///* ============================================================
250    // * JRobin : Pure java implementation of RRDTool's functionality
251    // * ============================================================
252    // *
253    // * Project Info:  http://www.jrobin.org
254    // * Project Lead:  Sasa Markovic (saxon@jrobin.org);
255    // *
256    // * (C) Copyright 2003-2005, by Sasa Markovic.
257    // *
258    // * Developers:    Sasa Markovic (saxon@jrobin.org)
259    // *
260    // *
261    // * This library is free software; you can redistribute it and/or modify it under the terms
262    // * of the GNU Lesser General Public License as published by the Free Software Foundation;
263    // * either version 2.1 of the License, or (at your option) any later version.
264    // *
265    // * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
266    // * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
267    // * See the GNU Lesser General Public License for more details.
268    // *
269    // * You should have received a copy of the GNU Lesser General Public License along with this
270    // * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
271    // * Boston, MA 02111-1307, USA.
272    // */
273    //package org.jrobin.core;
274    //
275    //import java.io.IOException;
276    //import java.util.HashMap;
277    //import java.util.Iterator;
278    //import java.util.LinkedHashMap;
279    //import java.util.Set;
280    //
281    ///**
282    // * Class to represent the pool of open RRD files.<p>
283    // *
284    // * To open already existing RRD file with JRobin, you have to create a
285    // * {@link org.jrobin.core.RrdDb RrdDb} object by specifying RRD file path
286    // * as constructor argument. This operation can be time consuming
287    // * especially with large RRD files with many datasources and
288    // * several long archives.<p>
289    // *
290    // * In a multithreaded environment you might probably need a reference to the
291    // * same RRD file from two different threads (RRD file updates are performed in
292    // * one thread but data fetching and graphing is performed in another one). To make
293    // * the RrdDb construction process more efficient it might be convenient to open all
294    // * RRD files in a centralized place. That's the purpose of RrdDbPool class.<p>
295    // *
296    // * How does it work? The typical usage scenario goes like this:<p>
297    // *
298    // * <pre>
299    // * // obtain instance to RrdDbPool object
300    // * RrdDbPool pool = RrdDbPool.getInstance();
301    // *
302    // * // request a reference to RrdDb object
303    // * String path = "some_relative_or_absolute_path_to_any_RRD_file";
304    // * RrdDb rrdDb = RrdDbPool.requestRrdDb(path);
305    // *
306    // * // reference obtained, do whatever you want with it...
307    // * ...
308    // * ...
309    // *
310    // * // once you don't need the reference, release it.
311    // * // DO NOT CALL rrdDb.close() - files no longer in use are eventually closed by the pool
312    // * pool.release(rrdDb);
313    // * </pre>
314    // *
315    // * It's that simple. When the reference is requested for the first time, RrdDbPool will open the RRD file
316    // * for you and make some internal note that the RRD file is used only once. When the reference
317    // * to the same file (same RRD file path) is requested for the second time, the same RrdDb
318    // * reference will be returned, and its usage count will be increased by one. When the
319    // * reference is released its usage count will be decremented by one.<p>
320    // *
321    // * When the reference count drops to zero, RrdDbPool will not close the underlying
322    // * RRD file immediatelly. Instead of it, it will be marked as 'eligible for closing'.
323    // * If someone request the same RRD file again (before it gets closed), the same
324    // * reference will be returned again.<p>
325    // *
326    // * RrdDbPool has a 'garbage collector' which runs in a separate, low-priority
327    // * thread and gets activated only when the number of RRD files kept in the
328    // * pool is too big (greater than number returned from {@link #getCapacity getCapacity()}).
329    // * Only RRD files with a reference count equal to zero
330    // * will be eligible for closing. Unreleased RrdDb references are never invalidated.
331    // * RrdDbPool object keeps track of the time when each RRD file
332    // * becomes eligible for closing so that the oldest RRD file gets closed first.<p>
333    // *
334    // * Initial RrdDbPool capacity is set to {@link #INITIAL_CAPACITY}. Use {@link #setCapacity(int)}
335    // * method to change it at any time.<p>
336    // *
337    // * <b>WARNING:</b>Never use close() method on the reference returned from the pool.
338    // * When the reference is no longer needed, return it to the pool with the
339    // * {@link #release(RrdDb) release()} method.<p>
340    // *
341    // * However, you are not forced to use RrdDbPool methods to obtain RrdDb references
342    // * to RRD files, 'ordinary' RrdDb constructors are still available. But RrdDbPool class
343    // * offers serious performance improvement especially in complex applications with many
344    // * threads and many simultaneously open RRD files.<p>
345    // *
346    // * The pool is thread-safe. Not that the {@link RrdDb} objects returned from the pool are
347    // * also thread-safe<p>
348    // *
349    // * You should know that each operating system has its own internal limit on the number
350    // * of simultaneously open files. The capacity of your RrdDbPool should be
351    // * reasonably smaller than the limit imposed by your operating system.<p>
352    // *
353    // * <b>WARNING:</b> The pool cannot be used to manipulate RrdDb objects
354    // * with {@link RrdBackend backends} different from default.<p>
355    // */
356    //public class RrdDbPool implements Runnable {
357    //      static final String GC_THREAD_NAME = "RrdDbPool GC thread";
358    //      static final String CLOSING_THREAD_NAME = "RrdDbPool closing thread";
359    //      private static final boolean DEBUG = false;
360    //
361    //      // singleton pattern
362    //      private static RrdDbPool ourInstance;
363    //      private boolean closingOnExit = true;
364    //
365    //      private Thread shutdownHook = new Thread(CLOSING_THREAD_NAME) {
366    //              public void run() {
367    //                      try {
368    //                              close();
369    //                      }
370    //                      catch (IOException e) {
371    //                              e.printStackTrace();
372    //                      }
373    //              }
374    //      };
375    //
376    //      /**
377    //       * Constant to represent the maximum number of internally open RRD files
378    //       * which still does not force garbage collector (the process which closes RRD files) to run.
379    //       */
380    //      public static final int INITIAL_CAPACITY = 500;
381    //      private int capacity = INITIAL_CAPACITY, maxUsedCapacity;
382    //      private boolean active = true;
383    //
384    //      /**
385    //       * Constant to represent the internal behaviour of the pool.
386    //       * Defaults to <code>true</code> but can be changed at runtime. See
387    //       * {@link #setLimitedCapacity(boolean)} for more information.
388    //       */
389    //      public static final boolean LIMITED_CAPACITY = false;
390    //      private boolean limitedCapacity = LIMITED_CAPACITY;
391    //
392    //      /**
393    //       * Constant to represent the priority of the background thread which closes excessive RRD files
394    //       * which are no longer in use.
395    //       */
396    //      public static final int GC_THREAD_PRIORITY = /** Thread.NORM_PRIORITY - */ 1;
397    //
398    //      private HashMap<String, RrdEntry> rrdMap = new HashMap<String, RrdEntry>(INITIAL_CAPACITY);
399    //      private LinkedHashMap<String, RrdEntry> rrdIdleMap = new LinkedHashMap<String, RrdEntry>(INITIAL_CAPACITY);
400    //      private RrdBackendFactory factory;
401    //      private int poolHitsCount = 0, poolRequestsCount = 0;
402    //
403    //      /**
404    //       * Returns an instance to RrdDbPool object. Only one such object may exist in each JVM.
405    //       *
406    //       * @return Instance to RrdDbPool object.
407    //       */
408    //      public synchronized static RrdDbPool getInstance() {
409    //              if (ourInstance == null) {
410    //                      ourInstance = new RrdDbPool();
411    //                      ourInstance.startGarbageCollector();
412    //              }
413    //              return ourInstance;
414    //      }
415    //
416    //      private RrdDbPool() {
417    //              setClosingOnExit(closingOnExit);
418    //      }
419    //
420    //      /**
421    //       * Checks the exiting behaviour of RrdDbPool.
422    //       * @return <code>True</code>, if all RRD files are to be closed
423    //       * when application invokes <code>System.exit()</code>.
424    //       * <code>False</code> otherwise. The default behaviour is <code>true</code>
425    //       * (all RRD files will be closed on exit).
426    //       */
427    //      public synchronized boolean isClosingOnExit() {
428    //              return closingOnExit;
429    //      }
430    //
431    //      /**
432    //       * Sets the exiting behaviour of RrdDbPool.
433    //       * @param closingOnExit <code>True</code>, if all RRD files are to be closed
434    //       * when application invokes <code>System.exit()</code>.
435    //       * <code>False</code> otherwise. The default behaviour is <code>true</code>
436    //       * (all RRD files will be closed on exit).
437    //       */
438    //      public synchronized void setClosingOnExit(boolean closingOnExit) {
439    //              Runtime runtime = Runtime.getRuntime();
440    //              runtime.removeShutdownHook(shutdownHook);
441    //              if(closingOnExit) {
442    //                      runtime.addShutdownHook(shutdownHook);
443    //              }
444    //              this.closingOnExit = closingOnExit;
445    //      }
446    //
447    //      private void startGarbageCollector() {
448    //              Thread gcThread = new Thread(this, GC_THREAD_NAME);
449    //              gcThread.setPriority(GC_THREAD_PRIORITY);
450    //              gcThread.setDaemon(true);
451    //              gcThread.start();
452    //      }
453    //
454    //      /**
455    //       * Returns a reference to an existing RRD file with the specified path.
456    //       * If the file is already open in the pool, existing reference to it will be returned.
457    //       * Otherwise, the file is open and a newly created reference to it is returned.
458    //       *
459    //       * @param path Relative or absolute path to a RRD file.
460    //       * @return Reference to a RrdDb object (RRD file).
461    //       * @throws IOException  Thrown in case of I/O error.
462    //       * @throws RrdException Thrown in case of JRobin specific error.
463    //       */
464    //      public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException {
465    //              proveActive();
466    //              poolRequestsCount++;
467    //              String canonicalPath = getCanonicalPath(path);
468    //              for(;;) {
469    //                      RrdEntry rrdEntry = rrdMap.get(canonicalPath);
470    //                      if (rrdEntry != null) {
471    //                              // already open, use it!
472    //                              reportUsage(canonicalPath, rrdEntry);
473    //                              poolHitsCount++;
474    ////                            debug("CACHED: " + rrdEntry.dump());
475    //                              return rrdEntry.getRrdDb();
476    //                      }
477    //                      else if(!limitedCapacity || rrdMap.size() < capacity) {
478    //                              // not found, open it
479    //                              RrdDb rrdDb = createRrdDb(path, null);
480    //                              rrdEntry = new RrdEntry(rrdDb);
481    //                              addRrdEntry(canonicalPath, rrdEntry);
482    ////                            debug("ADDED: " + rrdEntry.dump());
483    //                              return rrdDb;
484    //                      }
485    //                      else {
486    //                              // we have to wait
487    //                              try {
488    //                                      wait();
489    //                              }
490    //                              catch (InterruptedException e) {
491    //                                      throw new RrdException("Request for file '" + path + "' was interrupted");
492    //                              }
493    //                      }
494    //              }
495    //      }
496    //
497    //      /**
498    //       * Returns a reference to a new RRD file. The new file will have the specified
499    //       * relative or absolute path, and its contents will be provided from the specified
500    //       * XML file (RRDTool comaptible).
501    //       *
502    //       * @param path    Relative or absolute path to a new RRD file.
503    //       * @param xmlPath Relative or absolute path to an existing XML dump file (RRDTool comaptible)
504    //       * @return Reference to a RrdDb object (RRD file).
505    //       * @throws IOException  Thrown in case of I/O error.
506    //       * @throws RrdException Thrown in case of JRobin specific error.
507    //       */
508    //      public synchronized RrdDb requestRrdDb(String path, String xmlPath)
509    //                      throws IOException, RrdException {
510    //              return requestNewRrdDb(path, xmlPath);
511    //      }
512    //
513    //      /**
514    //       * Returns a reference to a new RRD file. The new file will be created based on the
515    //       * definition contained in a RrdDef object.
516    //       *
517    //       * @param rrdDef RRD definition object
518    //       * @return Reference to a RrdDb object (RRD file).
519    //       * @throws IOException  Thrown in case of I/O error.
520    //       * @throws RrdException Thrown in case of JRobin specific error.
521    //       */
522    //      public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException {
523    //              return requestNewRrdDb(rrdDef.getPath(), rrdDef);
524    //      }
525    //
526    //      private RrdDb requestNewRrdDb(String path, Object creationDef) throws IOException, RrdException {
527    //              proveActive();
528    //              poolRequestsCount++;
529    //              String canonicalPath = getCanonicalPath(path);
530    //              for(;;) {
531    //                      RrdEntry rrdEntry = rrdMap.get(canonicalPath);
532    //                      if(rrdEntry != null) {
533    //                              // already open
534    //                              removeIfIdle(canonicalPath, rrdEntry);
535    //                      }
536    //                      else if(!limitedCapacity || rrdMap.size() < capacity) {
537    //                              RrdDb rrdDb = createRrdDb(path, creationDef);
538    //                              RrdEntry newRrdEntry = new RrdEntry(rrdDb);
539    //                              addRrdEntry(canonicalPath, newRrdEntry);
540    ////                            debug("ADDED: " + newRrdEntry.dump());
541    //                              return rrdDb;
542    //                      }
543    //                      else {
544    //                               // we have to wait
545    //                              try {
546    //                                      wait();
547    //                              }
548    //                              catch (InterruptedException e) {
549    //                                      throw new RrdException("Request for file '" + path + "' was interrupted");
550    //                              }
551    //                      }
552    //              }
553    //      }
554    //
555    //      private RrdDb createRrdDb(String path, Object creationDef) throws RrdException, IOException {
556    //              if(creationDef == null) {
557    //                      // existing RRD
558    //                      return new RrdDb(path, getFactory());
559    //              }
560    //              else if(creationDef instanceof String) {
561    //                      // XML input
562    //                      return new RrdDb(path, (String) creationDef, getFactory());
563    //              }
564    //              else if(creationDef instanceof RrdDef) {
565    //                      // RrdDef
566    //                      return new RrdDb((RrdDef) creationDef, getFactory());
567    //              }
568    //              else {
569    //                      throw new RrdException("Unexpected input object type: " +
570    //                              creationDef.getClass().getName());
571    //              }
572    //      }
573    //
574    //      private void reportUsage(String canonicalPath, RrdEntry rrdEntry) {
575    //              if (rrdEntry.reportUsage() == 1) {
576    //                      // must not be garbage collected
577    //                      rrdIdleMap.remove(canonicalPath);
578    //              }
579    //      }
580    //
581    //      private void reportRelease(String canonicalPath, RrdEntry rrdEntry) {
582    //              if (rrdEntry.reportRelease() == 0) {
583    //                      // ready to be garbage collected
584    //                      rrdIdleMap.put(canonicalPath, rrdEntry);
585    //              }
586    //      }
587    //
588    //      private void addRrdEntry(String canonicalPath, RrdEntry newRrdEntry) {
589    //              rrdMap.put(canonicalPath, newRrdEntry);
590    //              maxUsedCapacity = Math.max(rrdMap.size(), maxUsedCapacity);
591    //              // notify waiting threads
592    //              notifyAll();
593    //      }
594    //
595    //      private void removeIfIdle(String canonicalPath, RrdEntry rrdEntry)
596    //                      throws RrdException, IOException {
597    //              // already open, check if active (not released)
598    //              if (rrdEntry.isInUse()) {
599    //                      // not released, not allowed here
600    //                      throw new RrdException("Cannot create new RrdDb file: " +
601    //                                      "File '" + canonicalPath + "' already in use");
602    //              } else {
603    //                      // open but released... safe to close it
604    ////                    debug("WILL BE RECREATED: " + rrdEntry.dump());
605    //                      removeRrdEntry(canonicalPath, rrdEntry);
606    //              }
607    //      }
608    //
609    //      private void removeRrdEntry(String canonicalPath, RrdEntry rrdEntry) throws IOException {
610    //              rrdEntry.closeRrdDb();
611    //              rrdMap.remove(canonicalPath);
612    //              rrdIdleMap.remove(canonicalPath);
613    ////            debug("REMOVED: " + rrdEntry.dump());
614    //      }
615    //
616    //      /**
617    //       * Method used to report that the reference to a RRD file is no longer needed. File that
618    //       * is no longer needed (all references to it are released) is marked 'eligible for
619    //       * closing'. It will be eventually closed by the pool when the number of open RRD files
620    //       * becomes too big. Most recently released files will be closed last.
621    //       *
622    //       * @param rrdDb Reference to RRD file that is no longer needed.
623    //       * @throws IOException  Thrown in case of I/O error.
624    //       * @throws RrdException Thrown in case of JRobin specific error.
625    //       */
626    //      public synchronized void release(RrdDb rrdDb) throws IOException, RrdException {
627    //              proveActive();
628    //              if (rrdDb == null) {
629    //                      // we don't want NullPointerException
630    //                      return;
631    //              }
632    //              if (rrdDb.isClosed()) {
633    //                      throw new RrdException("File " + rrdDb.getPath() + " already closed");
634    //              }
635    //              String canonicalPath = getCanonicalPath(rrdDb.getPath());
636    //              if (rrdMap.containsKey(canonicalPath)) {
637    //                      RrdEntry rrdEntry = rrdMap.get(canonicalPath);
638    //                      reportRelease(canonicalPath, rrdEntry);
639    ////                    debug("RELEASED: " + rrdEntry.dump());
640    //              } else {
641    //                      throw new RrdException("RRD file " + rrdDb.getPath() + " not in the pool");
642    //              }
643    //              // notify waiting threads
644    //              notifyAll();
645    //      }
646    //
647    //      /**
648    //       * This method runs garbage collector in a separate thread. If the number of
649    //       * open RRD files kept in the pool is too big (greater than number
650    //       * returned from {@link #getCapacity getCapacity()}), garbage collector will try
651    //       * to close and remove RRD files with a reference count equal to zero.
652    //       * Never call this method directly.
653    //       */
654    //      public void run() {
655    ////            debug("GC: started");
656    //              while (active) {
657    //                      synchronized (this) {
658    //                              if (rrdMap.size() >= capacity && rrdIdleMap.size() > 0) {
659    //                                      try {
660    //                                              String canonicalPath = rrdIdleMap.keySet().iterator().next();
661    //                                              RrdEntry rrdEntry = rrdIdleMap.get(canonicalPath);
662    ////                                            debug("GC: closing " + rrdEntry.dump());
663    //                                              removeRrdEntry(canonicalPath, rrdEntry);
664    //                                      } catch (IOException e) {
665    //                                              e.printStackTrace();
666    //                                      }
667    //                                      notifyAll();
668    //                              }
669    //                              else {
670    //                                      try {
671    ////                                            debug("GC: waiting");
672    //                                              wait();
673    ////                                            debug("GC: running");
674    //                                      } catch (InterruptedException e) {
675    //                                              e.printStackTrace();
676    //                                      }
677    //                              }
678    //                      }
679    //              }
680    //      }
681    //
682    //      protected void finalize() throws IOException {
683    //              close();
684    //      }
685    //
686    //      /**
687    //       * Clears the internal state of the pool entirely. All open RRD files are closed.
688    //       *
689    //       * @throws IOException Thrown in case of I/O related error.
690    //       */
691    //      public synchronized void reset() throws IOException {
692    //              Iterator<RrdEntry> it = rrdMap.values().iterator();
693    //              while (it.hasNext()) {
694    //                      RrdEntry rrdEntry = it.next();
695    //                      rrdEntry.closeRrdDb();
696    //              }
697    //              rrdMap.clear();
698    //              rrdIdleMap.clear();
699    ////            debug("Pool cleared");
700    //      }
701    //
702    //      /**
703    //       * Closes the pool and all RRD files currently held in the pool.
704    //       * No further operations on the pool are allowed.
705    //       * @throws IOException Thrown in case of I/O error.
706    //       */
707    //      public synchronized void close() throws IOException {
708    //              if(active) {
709    //                      active = false;
710    //                      reset();
711    ////                    debug("The pool is closed.");
712    //              }
713    //              else {
714    ////                    debug("The pool is already closed!");
715    //              }
716    //      }
717    //
718    //      private static String getCanonicalPath(String path) throws IOException {
719    //              return Util.getCanonicalPath(path);
720    //      }
721    //
722    //      static void debug(String msg) {
723    //              if (DEBUG) {
724    //                      System.out.println("POOL: " + msg);
725    //              }
726    //      }
727    //
728    //      /**
729    //       * Returns the internal state of the pool. Useful for debugging purposes.
730    //       *
731    //       * @param dumpFiles <code>true</code>, if dumped information should contain paths to open files
732    //       * currently held in the pool, <code>false</code> otherwise
733    //       * @return Internal pool state (with an optional list of open RRD files and
734    //       * the current number of usages for each one).
735    //       * @throws IOException Thrown in case of I/O error.
736    //       */
737    //      public synchronized String dump(boolean dumpFiles) throws IOException {
738    //              StringBuffer buff = new StringBuffer();
739    //              buff.append("==== POOL DUMP ===========================\n");
740    //              buff.append("open=" + rrdMap.size() + ", idle=" + rrdIdleMap.size() + "\n");
741    //              buff.append("capacity=" + capacity + ", " + "maxUsedCapacity=" + maxUsedCapacity + "\n");
742    //              buff.append("hits=" + poolHitsCount + ", " + "requests=" + poolRequestsCount + "\n");
743    //              buff.append("efficiency=" + getPoolEfficiency() + "\n");
744    //              if(dumpFiles) {
745    //                      buff.append("---- CACHED FILES ------------------------\n");
746    //                      Iterator<RrdEntry> it = rrdMap.values().iterator();
747    //                      while (it.hasNext()) {
748    //                              RrdEntry rrdEntry = it.next();
749    //                              buff.append(rrdEntry.dump() + "\n");
750    //                      }
751    //              }
752    //              return buff.toString();
753    //      }
754    //
755    //      /**
756    //       * Returns the complete internal state of the pool. Useful for debugging purposes.
757    //       *
758    //       * @return Internal pool state (with a list of open RRD files and the current number of
759    //       * usages for each one).
760    //       * @throws IOException Thrown in case of I/O error.
761    //       */
762    //      public synchronized String dump() throws IOException {
763    //              return dump(true);
764    //      }
765    //
766    //      /**
767    //       * Returns paths to all open files currently held in the pool.
768    //       * @return An array containing open file paths.
769    //       */
770    //      public synchronized String[] getCachedFilePaths() {
771    //              Set<String> keySet = rrdMap.keySet();
772    //              int n = keySet.size(), i = 0;
773    //              String[] files = new String[n];
774    //              Iterator<String> it = keySet.iterator();
775    //              while(it.hasNext()) {
776    //                      files[i++] = it.next();
777    //              }
778    //              return files;
779    //      }
780    //
781    //      /**
782    //       * Returns maximum number of internally open RRD files
783    //       * which still does not force garbage collector to run.
784    //       *
785    //       * @return Desired nuber of open files held in the pool.
786    //       */
787    //      public synchronized int getCapacity() {
788    //              return capacity;
789    //      }
790    //
791    //      /**
792    //       * Sets maximum number of internally open RRD files
793    //       * which still does not force garbage collector to run.
794    //       *
795    //       * @param capacity Desired number of open files to hold in the pool
796    //       */
797    //      public synchronized void setCapacity(int capacity) {
798    //              this.capacity = capacity;
799    ////            debug("Capacity set to: " + capacity);
800    //      }
801    //
802    //      private RrdBackendFactory getFactory() throws RrdException {
803    //              if (factory == null) {
804    //                      factory = RrdBackendFactory.getDefaultFactory();
805    //                      if (!(factory instanceof RrdFileBackendFactory)) {
806    //                              factory = null;
807    //                              throw new RrdException(
808    //                                      "RrdDbPool cannot work with factories not derived from RrdFileBackendFactory");
809    //                      }
810    //              }
811    //              return factory;
812    //      }
813    //
814    //      private class RrdEntry {
815    //              private RrdDb rrdDb;
816    //              private int usageCount = 1;
817    //
818    //              public RrdEntry(RrdDb rrdDb) {
819    //                      this.rrdDb = rrdDb;
820    //              }
821    //
822    //              RrdDb getRrdDb() {
823    //                      return rrdDb;
824    //              }
825    //
826    //              int reportUsage() {
827    //                      assert usageCount >= 0: "Unexpected reportUsage count: " + usageCount;
828    //                      return ++usageCount;
829    //              }
830    //
831    //              int reportRelease() {
832    //                      assert usageCount > 0: "Unexpected reportRelease count: " + usageCount;
833    //                      return --usageCount;
834    //              }
835    //
836    //              boolean isInUse() {
837    //                      return usageCount > 0;
838    //              }
839    //
840    //              void closeRrdDb() throws IOException {
841    //                      rrdDb.close();
842    //              }
843    //
844    //              String dump() throws IOException {
845    //                      String canonicalPath = getCanonicalPath(rrdDb.getPath());
846    //                      return canonicalPath + " [" + usageCount + "]";
847    //              }
848    //      }
849    //
850    //      /**
851    //       * Calculates pool's efficency ratio. The ratio is obtained by dividing the number of
852    //       * RrdDb requests served from the internal pool of open RRD files
853    //       * with the number of total RrdDb requests.
854    //       *
855    //       * @return Pool's efficiency ratio as a double between 1 (best) and 0 (worst).
856    //       * If no RrdDb reference was ever requested, 1 would be returned.
857    //       */
858    //      public synchronized double getPoolEfficiency() {
859    //              if (poolRequestsCount == 0) {
860    //                      return 1.0;
861    //              }
862    //              double ratio = (double) poolHitsCount / (double) poolRequestsCount;
863    //              // round to 3 decimal digits
864    //              return Math.round(ratio * 1000.0) / 1000.0;
865    //      }
866    //
867    //      /**
868    //       * Returns the number of RRD requests served from the internal pool of open RRD files
869    //       *
870    //       * @return The number of pool "hits".
871    //       */
872    //      public synchronized int getPoolHitsCount() {
873    //              return poolHitsCount;
874    //      }
875    //
876    //      /**
877    //       * Returns the total number of RRD requests successfully served by this pool.
878    //       *
879    //       * @return Total number of RRD requests
880    //       */
881    //      public synchronized int getPoolRequestsCount() {
882    //              return poolRequestsCount;
883    //      }
884    //
885    //      /**
886    //       * Returns the maximum number of open RRD files over the lifetime
887    //       * of the pool.
888    //       * @return maximum number of open RRD files.
889    //       */
890    //      public synchronized int getMaxUsedCapacity() {
891    //              return maxUsedCapacity;
892    //      }
893    //
894    //      /**
895    //       * Checks the internal behaviour of the pool. See {@link #setLimitedCapacity(boolean)} for
896    //       * more information.
897    //       *
898    //       * @return <code>true</code> if the pool is 'flexible' (by not imposing the strict
899    //       * limit on the number of simultaneously open files), <code>false</code> otherwise.
900    //       */
901    //      public synchronized boolean isLimitedCapacity() {
902    //              return limitedCapacity;
903    //      }
904    //
905    //      /**
906    //       * Sets the behaviour of the pool. If <code>true</code> is passed as argument, the pool will never
907    //       * open more than {@link #getCapacity()} files at any time. If set to <code>false</code>,
908    //       * the pool might keep more open files, but only for a short period of time. This method might be
909    //       * useful if you want avoid OS limits when it comes to the number of simultaneously open files.<p>
910    //       *
911    //       * By default, the pool behaviour is 'flexible' (<code>limitedCapacity</code> property defaults
912    //       * to false<p>
913    //       *
914    //       * @param limitedCapacity <code>true</code> if the pool should be 'flexible' (not imposing the strict
915    //       * limit on the number of simultaneously open files), <code>false</code> otherwise.
916    //       */
917    //      public synchronized void setLimitedCapacity(boolean limitedCapacity) {
918    //              this.limitedCapacity = limitedCapacity;
919    //      }
920    //
921    //      private void proveActive() throws IOException {
922    //              if(!active) {
923    //                      throw new IOException("RrdDbPool is already closed");
924    //              }
925    //      }
926    //
927    //      /**
928    //       * Checks if the pool is active. You can request RrdDb references only from the active pool. The
929    //       * pool is deactived when the {@link #close()} method is called.
930    //       * @return <code>true</code> if active, <code>false</code> otherwise.
931    //       */
932    //      public synchronized boolean isActive() {
933    //              return active;
934    //      }
935    //}
936    //
937    
938