/*******************************************************************************
 * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
 * Copyright (c) 2011 The OpenNMS Group, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *******************************************************************************/

package org.jrobin.core;

import java.io.IOException;
import java.util.HashMap;

import org.jrobin.core.RrdException;

/**
 * This class should be used to synchronize access to RRD files
 * in a multithreaded environment. This class should be also used to prevent openning of
 * too many RRD files at the same time (thus avoiding operating system limits)
 */

public class RrdDbPool {
	/**
	 * Initial capacity of the pool i.e. maximum number of simultaneously open RRD files. The pool will
	 * never open too many RRD files at the same time.
	 */
	public static final int INITIAL_CAPACITY = 200;
	private static RrdDbPool instance;

	private int capacity = INITIAL_CAPACITY;
	private HashMap<String, RrdEntry> rrdMap = new HashMap<String, RrdEntry>(INITIAL_CAPACITY);

	/**
	 * Creates a single instance of the class on the first call, or returns already existing one.
	 *
	 * @return Single instance of this class
	 * @throws RrdException Thrown if the default RRD backend is not derived from the {@link RrdFileBackendFactory}
	 */
	public synchronized static RrdDbPool getInstance() throws RrdException {
		if (instance == null) {
			instance = new RrdDbPool();
		}
		return instance;
	}

	private RrdDbPool() throws RrdException {
		RrdBackendFactory factory = RrdBackendFactory.getDefaultFactory();
		if (!(factory instanceof RrdFileBackendFactory)) {
			throw new RrdException("Cannot create instance of " + getClass().getName() + " with " +
					"a default backend factory not derived from RrdFileBackendFactory");
		}
	}

	/**
	 * Requests a RrdDb reference for the given RRD file path.<p>
	 * <ul>
	 * <li>If the file is already open, previously returned RrdDb reference will be returned. Its usage count
	 * will be incremented by one.
	 * <li>If the file is not already open and the number of already open RRD files is less than
	 * {@link #INITIAL_CAPACITY}, the file will be open and a new RrdDb reference will be returned.
	 * If the file is not already open and the number of already open RRD files is equal to
	 * {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
	 * </ul>
	 *
	 * @param path Path to existing RRD file
	 * @return reference for the give RRD file
	 * @throws IOException  Thrown in case of I/O error
	 * @throws RrdException Thrown in case of JRobin specific error
	 */
	public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException {
		String canonicalPath = Util.getCanonicalPath(path);
		while (!rrdMap.containsKey(canonicalPath) && rrdMap.size() >= capacity) {
			try {
				wait();
			}
			catch (InterruptedException e) {
				throw new RrdException(e);
			}
		}
		if (rrdMap.containsKey(canonicalPath)) {
			// already open, just increase usage count
			RrdEntry entry = rrdMap.get(canonicalPath);
			entry.count++;
			return entry.rrdDb;
		}
		else {
			// not open, open it now and add to the map
			RrdDb rrdDb = new RrdDb(canonicalPath);
			rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
			return rrdDb;
		}
	}

	/**
	 * Requests a RrdDb reference for the given RRD file definition object.<p>
	 * <ul>
	 * <li>If the file with the path specified in the RrdDef object is already open,
	 * the method blocks until the file is closed.
	 * <li>If the file is not already open and the number of already open RRD files is less than
	 * {@link #INITIAL_CAPACITY}, a new RRD file will be created and a its RrdDb reference will be returned.
	 * If the file is not already open and the number of already open RRD files is equal to
	 * {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
	 * </ul>
	 *
	 * @param rrdDef Definition of the RRD file to be created
	 * @return Reference to the newly created RRD file
	 * @throws IOException  Thrown in case of I/O error
	 * @throws RrdException Thrown in case of JRobin specific error
	 */
	public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException {
		String canonicalPath = Util.getCanonicalPath(rrdDef.getPath());
		while (rrdMap.containsKey(canonicalPath) || rrdMap.size() >= capacity) {
			try {
				wait();
			}
			catch (InterruptedException e) {
				throw new RrdException(e);
			}
		}
		RrdDb rrdDb = new RrdDb(rrdDef);
		rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
		return rrdDb;
	}

	/**
	 * Requests a RrdDb reference for the given path. The file will be created from
	 * external data (from XML dump, RRD file or RRDTool's binary RRD file).<p>
	 * <ul>
	 * <li>If the file with the path specified is already open,
	 * the method blocks until the file is closed.
	 * <li>If the file is not already open and the number of already open RRD files is less than
	 * {@link #INITIAL_CAPACITY}, a new RRD file will be created and a its RrdDb reference will be returned.
	 * If the file is not already open and the number of already open RRD files is equal to
	 * {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
	 * </ul>
	 *
	 * @param path	   Path to RRD file which should be created
	 * @param sourcePath Path to external data which is to be converted to JRobin's native RRD file format
	 * @return Reference to the newly created RRD file
	 * @throws IOException  Thrown in case of I/O error
	 * @throws RrdException Thrown in case of JRobin specific error
	 */
	public synchronized RrdDb requestRrdDb(String path, String sourcePath)
			throws IOException, RrdException,RrdException {
		String canonicalPath = Util.getCanonicalPath(path);
		while (rrdMap.containsKey(canonicalPath) || rrdMap.size() >= capacity) {
			try {
				wait();
			}
			catch (InterruptedException e) {
				throw new RrdException(e);
			}
		}
		RrdDb rrdDb = new RrdDb(canonicalPath, sourcePath);
		rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
		return rrdDb;
	}

	/**
	 * Releases RrdDb reference previously obtained from the pool. When a reference is released, its usage
	 * count is decremented by one. If usage count drops to zero, the underlying RRD file will be closed.
	 *
	 * @param rrdDb RrdDb reference to be returned to the pool
	 * @throws IOException  Thrown in case of I/O error
	 * @throws RrdException Thrown in case of JRobin specific error
	 */
	public synchronized void release(RrdDb rrdDb) throws IOException, RrdException {
		// null pointer should not kill the thread, just ignore it
		if (rrdDb == null) {
			return;
		}
		String canonicalPath = Util.getCanonicalPath(rrdDb.getPath());
		if (!rrdMap.containsKey(canonicalPath)) {
			throw new RrdException("Could not release [" + canonicalPath + "], the file was never requested");
		}
		RrdEntry entry = rrdMap.get(canonicalPath);
		if (--entry.count <= 0) {
			// no longer used
			rrdMap.remove(canonicalPath);
			notifyAll();
			entry.rrdDb.close();
		}
	}

	/**
	 * Returns the maximum number of simultaneously open RRD files.
	 *
	 * @return maximum number of simultaneously open RRD files
	 */
	public synchronized int getCapacity() {
		return capacity;
	}

	/**
	 * Sets the maximum number of simultaneously open RRD files.
	 *
	 * @param capacity Maximum number of simultaneously open RRD files.
	 */
	public synchronized void setCapacity(int capacity) {
		this.capacity = capacity;
	}

	/**
	 * Returns an array of open file names.
	 *
	 * @return Array with canonical paths to open RRD files held in the pool.
	 */
	public synchronized String[] getOpenFiles() {
		return rrdMap.keySet().toArray(new String[0]);
	}

	/**
	 * Returns the number of open RRD files.
	 *
	 * @return Number of currently open RRD files held in the pool.
	 */
	public synchronized int getOpenFileCount() {
		return rrdMap.size();
	}

	private final static class RrdEntry {
		RrdDb rrdDb;
		int count;

		RrdEntry(final RrdDb rrdDb) {
			this.rrdDb = rrdDb;
			this.count = 1;
		}
	}
}

// OLDER VERSION IS HERE

///* ============================================================
// * JRobin : Pure java implementation of RRDTool's functionality
// * ============================================================
// *
// * Project Info:  http://www.jrobin.org
// * Project Lead:  Sasa Markovic (saxon@jrobin.org);
// *
// * (C) Copyright 2003-2005, by Sasa Markovic.
// *
// * Developers:    Sasa Markovic (saxon@jrobin.org)
// *
// *
// * This library is free software; you can redistribute it and/or modify it under the terms
// * of the GNU Lesser General Public License as published by the Free Software Foundation;
// * either version 2.1 of the License, or (at your option) any later version.
// *
// * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// * See the GNU Lesser General Public License for more details.
// *
// * You should have received a copy of the GNU Lesser General Public License along with this
// * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
// * Boston, MA 02111-1307, USA.
// */
//package org.jrobin.core;
//
//import java.io.IOException;
//import java.util.HashMap;
//import java.util.Iterator;
//import java.util.LinkedHashMap;
//import java.util.Set;
//
///**
// * Class to represent the pool of open RRD files.<p>
// *
// * To open already existing RRD file with JRobin, you have to create a
// * {@link org.jrobin.core.RrdDb RrdDb} object by specifying RRD file path
// * as constructor argument. This operation can be time consuming
// * especially with large RRD files with many datasources and
// * several long archives.<p>
// *
// * In a multithreaded environment you might probably need a reference to the
// * same RRD file from two different threads (RRD file updates are performed in
// * one thread but data fetching and graphing is performed in another one). To make
// * the RrdDb construction process more efficient it might be convenient to open all
// * RRD files in a centralized place. That's the purpose of RrdDbPool class.<p>
// *
// * How does it work? The typical usage scenario goes like this:<p>
// *
// * <pre>
// * // obtain instance to RrdDbPool object
// * RrdDbPool pool = RrdDbPool.getInstance();
// *
// * // request a reference to RrdDb object
// * String path = "some_relative_or_absolute_path_to_any_RRD_file";
// * RrdDb rrdDb = RrdDbPool.requestRrdDb(path);
// *
// * // reference obtained, do whatever you want with it...
// * ...
// * ...
// *
// * // once you don't need the reference, release it.
// * // DO NOT CALL rrdDb.close() - files no longer in use are eventually closed by the pool
// * pool.release(rrdDb);
// * </pre>
// *
// * It's that simple. When the reference is requested for the first time, RrdDbPool will open the RRD file
// * for you and make some internal note that the RRD file is used only once. When the reference
// * to the same file (same RRD file path) is requested for the second time, the same RrdDb
// * reference will be returned, and its usage count will be increased by one. When the
// * reference is released its usage count will be decremented by one.<p>
// *
// * When the reference count drops to zero, RrdDbPool will not close the underlying
// * RRD file immediatelly. Instead of it, it will be marked as 'eligible for closing'.
// * If someone request the same RRD file again (before it gets closed), the same
// * reference will be returned again.<p>
// *
// * RrdDbPool has a 'garbage collector' which runs in a separate, low-priority
// * thread and gets activated only when the number of RRD files kept in the
// * pool is too big (greater than number returned from {@link #getCapacity getCapacity()}).
// * Only RRD files with a reference count equal to zero
// * will be eligible for closing. Unreleased RrdDb references are never invalidated.
// * RrdDbPool object keeps track of the time when each RRD file
// * becomes eligible for closing so that the oldest RRD file gets closed first.<p>
// *
// * Initial RrdDbPool capacity is set to {@link #INITIAL_CAPACITY}. Use {@link #setCapacity(int)}
// * method to change it at any time.<p>
// *
// * <b>WARNING:</b>Never use close() method on the reference returned from the pool.
// * When the reference is no longer needed, return it to the pool with the
// * {@link #release(RrdDb) release()} method.<p>
// *
// * However, you are not forced to use RrdDbPool methods to obtain RrdDb references
// * to RRD files, 'ordinary' RrdDb constructors are still available. But RrdDbPool class
// * offers serious performance improvement especially in complex applications with many
// * threads and many simultaneously open RRD files.<p>
// *
// * The pool is thread-safe. Not that the {@link RrdDb} objects returned from the pool are
// * also thread-safe<p>
// *
// * You should know that each operating system has its own internal limit on the number
// * of simultaneously open files. The capacity of your RrdDbPool should be
// * reasonably smaller than the limit imposed by your operating system.<p>
// *
// * <b>WARNING:</b> The pool cannot be used to manipulate RrdDb objects
// * with {@link RrdBackend backends} different from default.<p>
// */
//public class RrdDbPool implements Runnable {
//	static final String GC_THREAD_NAME = "RrdDbPool GC thread";
//	static final String CLOSING_THREAD_NAME = "RrdDbPool closing thread";
//	private static final boolean DEBUG = false;
//
//	// singleton pattern
//	private static RrdDbPool ourInstance;
//	private boolean closingOnExit = true;
//
//	private Thread shutdownHook = new Thread(CLOSING_THREAD_NAME) {
//		public void run() {
//			try {
//				close();
//			}
//			catch (IOException e) {
//				e.printStackTrace();
//			}
//		}
//	};
//
//	/**
//	 * Constant to represent the maximum number of internally open RRD files
//	 * which still does not force garbage collector (the process which closes RRD files) to run.
//	 */
//	public static final int INITIAL_CAPACITY = 500;
//	private int capacity = INITIAL_CAPACITY, maxUsedCapacity;
//	private boolean active = true;
//
//	/**
//	 * Constant to represent the internal behaviour of the pool.
//	 * Defaults to <code>true</code> but can be changed at runtime. See
//	 * {@link #setLimitedCapacity(boolean)} for more information.
//	 */
//	public static final boolean LIMITED_CAPACITY = false;
//	private boolean limitedCapacity = LIMITED_CAPACITY;
//
//	/**
//	 * Constant to represent the priority of the background thread which closes excessive RRD files
//	 * which are no longer in use.
//	 */
//	public static final int GC_THREAD_PRIORITY = /** Thread.NORM_PRIORITY - */ 1;
//
//	private HashMap<String, RrdEntry> rrdMap = new HashMap<String, RrdEntry>(INITIAL_CAPACITY);
//	private LinkedHashMap<String, RrdEntry> rrdIdleMap = new LinkedHashMap<String, RrdEntry>(INITIAL_CAPACITY);
//	private RrdBackendFactory factory;
//	private int poolHitsCount = 0, poolRequestsCount = 0;
//
//	/**
//	 * Returns an instance to RrdDbPool object. Only one such object may exist in each JVM.
//	 *
//	 * @return Instance to RrdDbPool object.
//	 */
//	public synchronized static RrdDbPool getInstance() {
//		if (ourInstance == null) {
//			ourInstance = new RrdDbPool();
//			ourInstance.startGarbageCollector();
//		}
//		return ourInstance;
//	}
//
//	private RrdDbPool() {
//		setClosingOnExit(closingOnExit);
//	}
//
//	/**
//	 * Checks the exiting behaviour of RrdDbPool.
//	 * @return <code>True</code>, if all RRD files are to be closed
//	 * when application invokes <code>System.exit()</code>.
//	 * <code>False</code> otherwise. The default behaviour is <code>true</code>
//	 * (all RRD files will be closed on exit).
//	 */
//	public synchronized boolean isClosingOnExit() {
//		return closingOnExit;
//	}
//
//	/**
//	 * Sets the exiting behaviour of RrdDbPool.
//	 * @param closingOnExit <code>True</code>, if all RRD files are to be closed
//	 * when application invokes <code>System.exit()</code>.
//	 * <code>False</code> otherwise. The default behaviour is <code>true</code>
//	 * (all RRD files will be closed on exit).
//	 */
//	public synchronized void setClosingOnExit(boolean closingOnExit) {
//		Runtime runtime = Runtime.getRuntime();
//		runtime.removeShutdownHook(shutdownHook);
//		if(closingOnExit) {
//			runtime.addShutdownHook(shutdownHook);
//		}
//		this.closingOnExit = closingOnExit;
//	}
//
//	private void startGarbageCollector() {
//		Thread gcThread = new Thread(this, GC_THREAD_NAME);
//		gcThread.setPriority(GC_THREAD_PRIORITY);
//		gcThread.setDaemon(true);
//		gcThread.start();
//	}
//
//	/**
//	 * Returns a reference to an existing RRD file with the specified path.
//	 * If the file is already open in the pool, existing reference to it will be returned.
//	 * Otherwise, the file is open and a newly created reference to it is returned.
//	 *
//	 * @param path Relative or absolute path to a RRD file.
//	 * @return Reference to a RrdDb object (RRD file).
//	 * @throws IOException  Thrown in case of I/O error.
//	 * @throws RrdException Thrown in case of JRobin specific error.
//	 */
//	public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException {
//		proveActive();
//		poolRequestsCount++;
//		String canonicalPath = getCanonicalPath(path);
//		for(;;) {
//			RrdEntry rrdEntry = rrdMap.get(canonicalPath);
//			if (rrdEntry != null) {
//				// already open, use it!
//				reportUsage(canonicalPath, rrdEntry);
//				poolHitsCount++;
////				debug("CACHED: " + rrdEntry.dump());
//				return rrdEntry.getRrdDb();
//			}
//			else if(!limitedCapacity || rrdMap.size() < capacity) {
//				// not found, open it
//				RrdDb rrdDb = createRrdDb(path, null);
//				rrdEntry = new RrdEntry(rrdDb);
//				addRrdEntry(canonicalPath, rrdEntry);
////				debug("ADDED: " + rrdEntry.dump());
//				return rrdDb;
//			}
//			else {
//				// we have to wait
//				try {
//					wait();
//				}
//				catch (InterruptedException e) {
//					throw new RrdException("Request for file '" + path + "' was interrupted");
//				}
//			}
//		}
//	}
//
//	/**
//	 * Returns a reference to a new RRD file. The new file will have the specified
//	 * relative or absolute path, and its contents will be provided from the specified
//	 * XML file (RRDTool comaptible).
//	 *
//	 * @param path    Relative or absolute path to a new RRD file.
//	 * @param xmlPath Relative or absolute path to an existing XML dump file (RRDTool comaptible)
//	 * @return Reference to a RrdDb object (RRD file).
//	 * @throws IOException  Thrown in case of I/O error.
//	 * @throws RrdException Thrown in case of JRobin specific error.
//	 */
//	public synchronized RrdDb requestRrdDb(String path, String xmlPath)
//			throws IOException, RrdException {
//		return requestNewRrdDb(path, xmlPath);
//	}
//
//	/**
//	 * Returns a reference to a new RRD file. The new file will be created based on the
//	 * definition contained in a RrdDef object.
//	 *
//	 * @param rrdDef RRD definition object
//	 * @return Reference to a RrdDb object (RRD file).
//	 * @throws IOException  Thrown in case of I/O error.
//	 * @throws RrdException Thrown in case of JRobin specific error.
//	 */
//	public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException {
//		return requestNewRrdDb(rrdDef.getPath(), rrdDef);
//	}
//
//	private RrdDb requestNewRrdDb(String path, Object creationDef) throws IOException, RrdException {
//		proveActive();
//		poolRequestsCount++;
//		String canonicalPath = getCanonicalPath(path);
//		for(;;) {
//			RrdEntry rrdEntry = rrdMap.get(canonicalPath);
//			if(rrdEntry != null) {
//				// already open
//				removeIfIdle(canonicalPath, rrdEntry);
//			}
//			else if(!limitedCapacity || rrdMap.size() < capacity) {
//				RrdDb rrdDb = createRrdDb(path, creationDef);
//				RrdEntry newRrdEntry = new RrdEntry(rrdDb);
//				addRrdEntry(canonicalPath, newRrdEntry);
////				debug("ADDED: " + newRrdEntry.dump());
//				return rrdDb;
//			}
//			else {
//				 // we have to wait
//				try {
//					wait();
//				}
//				catch (InterruptedException e) {
//					throw new RrdException("Request for file '" + path + "' was interrupted");
//				}
//			}
//		}
//	}
//
//	private RrdDb createRrdDb(String path, Object creationDef) throws RrdException, IOException {
//		if(creationDef == null) {
//			// existing RRD
//			return new RrdDb(path, getFactory());
//		}
//		else if(creationDef instanceof String) {
//			// XML input
//			return new RrdDb(path, (String) creationDef, getFactory());
//		}
//		else if(creationDef instanceof RrdDef) {
//			// RrdDef
//			return new RrdDb((RrdDef) creationDef, getFactory());
//		}
//		else {
//			throw new RrdException("Unexpected input object type: " +
//				creationDef.getClass().getName());
//		}
//	}
//
//	private void reportUsage(String canonicalPath, RrdEntry rrdEntry) {
//		if (rrdEntry.reportUsage() == 1) {
//			// must not be garbage collected
//			rrdIdleMap.remove(canonicalPath);
//		}
//	}
//
//	private void reportRelease(String canonicalPath, RrdEntry rrdEntry) {
//		if (rrdEntry.reportRelease() == 0) {
//			// ready to be garbage collected
//			rrdIdleMap.put(canonicalPath, rrdEntry);
//		}
//	}
//
//	private void addRrdEntry(String canonicalPath, RrdEntry newRrdEntry) {
//		rrdMap.put(canonicalPath, newRrdEntry);
//		maxUsedCapacity = Math.max(rrdMap.size(), maxUsedCapacity);
//		// notify waiting threads
//		notifyAll();
//	}
//
//	private void removeIfIdle(String canonicalPath, RrdEntry rrdEntry)
//			throws RrdException, IOException {
//		// already open, check if active (not released)
//		if (rrdEntry.isInUse()) {
//			// not released, not allowed here
//			throw new RrdException("Cannot create new RrdDb file: " +
//					"File '" + canonicalPath + "' already in use");
//		} else {
//			// open but released... safe to close it
////			debug("WILL BE RECREATED: " + rrdEntry.dump());
//			removeRrdEntry(canonicalPath, rrdEntry);
//		}
//	}
//
//	private void removeRrdEntry(String canonicalPath, RrdEntry rrdEntry) throws IOException {
//		rrdEntry.closeRrdDb();
//		rrdMap.remove(canonicalPath);
//		rrdIdleMap.remove(canonicalPath);
////		debug("REMOVED: " + rrdEntry.dump());
//	}
//
//	/**
//	 * Method used to report that the reference to a RRD file is no longer needed. File that
//	 * is no longer needed (all references to it are released) is marked 'eligible for
//	 * closing'. It will be eventually closed by the pool when the number of open RRD files
//	 * becomes too big. Most recently released files will be closed last.
//	 *
//	 * @param rrdDb Reference to RRD file that is no longer needed.
//	 * @throws IOException  Thrown in case of I/O error.
//	 * @throws RrdException Thrown in case of JRobin specific error.
//	 */
//	public synchronized void release(RrdDb rrdDb) throws IOException, RrdException {
//		proveActive();
//		if (rrdDb == null) {
//			// we don't want NullPointerException
//			return;
//		}
//		if (rrdDb.isClosed()) {
//			throw new RrdException("File " + rrdDb.getPath() + " already closed");
//		}
//		String canonicalPath = getCanonicalPath(rrdDb.getPath());
//		if (rrdMap.containsKey(canonicalPath)) {
//			RrdEntry rrdEntry = rrdMap.get(canonicalPath);
//			reportRelease(canonicalPath, rrdEntry);
////			debug("RELEASED: " + rrdEntry.dump());
//		} else {
//			throw new RrdException("RRD file " + rrdDb.getPath() + " not in the pool");
//		}
//		// notify waiting threads
//		notifyAll();
//	}
//
//	/**
//	 * This method runs garbage collector in a separate thread. If the number of
//	 * open RRD files kept in the pool is too big (greater than number
//	 * returned from {@link #getCapacity getCapacity()}), garbage collector will try
//	 * to close and remove RRD files with a reference count equal to zero.
//	 * Never call this method directly.
//	 */
//	public void run() {
////		debug("GC: started");
//		while (active) {
//			synchronized (this) {
//				if (rrdMap.size() >= capacity && rrdIdleMap.size() > 0) {
//					try {
//						String canonicalPath = rrdIdleMap.keySet().iterator().next();
//						RrdEntry rrdEntry = rrdIdleMap.get(canonicalPath);
////						debug("GC: closing " + rrdEntry.dump());
//						removeRrdEntry(canonicalPath, rrdEntry);
//					} catch (IOException e) {
//						e.printStackTrace();
//					}
//					notifyAll();
//				}
//				else {
//					try {
////						debug("GC: waiting");
//						wait();
////						debug("GC: running");
//					} catch (InterruptedException e) {
//						e.printStackTrace();
//					}
//				}
//			}
//		}
//	}
//
//	protected void finalize() throws IOException {
//		close();
//	}
//
//	/**
//	 * Clears the internal state of the pool entirely. All open RRD files are closed.
//	 *
//	 * @throws IOException Thrown in case of I/O related error.
//	 */
//	public synchronized void reset() throws IOException {
//		Iterator<RrdEntry> it = rrdMap.values().iterator();
//		while (it.hasNext()) {
//			RrdEntry rrdEntry = it.next();
//			rrdEntry.closeRrdDb();
//		}
//		rrdMap.clear();
//		rrdIdleMap.clear();
////		debug("Pool cleared");
//	}
//
//	/**
//	 * Closes the pool and all RRD files currently held in the pool.
//	 * No further operations on the pool are allowed.
//	 * @throws IOException Thrown in case of I/O error.
//	 */
//	public synchronized void close() throws IOException {
//		if(active) {
//			active = false;
//			reset();
////			debug("The pool is closed.");
//		}
//		else {
////			debug("The pool is already closed!");
//		}
//	}
//
//	private static String getCanonicalPath(String path) throws IOException {
//		return Util.getCanonicalPath(path);
//	}
//
//	static void debug(String msg) {
//		if (DEBUG) {
//			System.out.println("POOL: " + msg);
//		}
//	}
//
//	/**
//	 * Returns the internal state of the pool. Useful for debugging purposes.
//	 *
//	 * @param dumpFiles <code>true</code>, if dumped information should contain paths to open files
//	 * currently held in the pool, <code>false</code> otherwise
//	 * @return Internal pool state (with an optional list of open RRD files and
//	 * the current number of usages for each one).
//	 * @throws IOException Thrown in case of I/O error.
//	 */
//	public synchronized String dump(boolean dumpFiles) throws IOException {
//		StringBuffer buff = new StringBuffer();
//		buff.append("==== POOL DUMP ===========================\n");
//		buff.append("open=" + rrdMap.size() + ", idle=" + rrdIdleMap.size() + "\n");
//		buff.append("capacity=" + capacity + ", " + "maxUsedCapacity=" + maxUsedCapacity + "\n");
//		buff.append("hits=" + poolHitsCount + ", " + "requests=" + poolRequestsCount + "\n");
//		buff.append("efficiency=" + getPoolEfficiency() + "\n");
//		if(dumpFiles) {
//			buff.append("---- CACHED FILES ------------------------\n");
//			Iterator<RrdEntry> it = rrdMap.values().iterator();
//			while (it.hasNext()) {
//				RrdEntry rrdEntry = it.next();
//				buff.append(rrdEntry.dump() + "\n");
//			}
//		}
//		return buff.toString();
//	}
//
//	/**
//	 * Returns the complete internal state of the pool. Useful for debugging purposes.
//	 *
//	 * @return Internal pool state (with a list of open RRD files and the current number of
//	 * usages for each one).
//	 * @throws IOException Thrown in case of I/O error.
//	 */
//	public synchronized String dump() throws IOException {
//		return dump(true);
//	}
//
//	/**
//	 * Returns paths to all open files currently held in the pool.
//	 * @return An array containing open file paths.
//	 */
//	public synchronized String[] getCachedFilePaths() {
//		Set<String> keySet = rrdMap.keySet();
//		int n = keySet.size(), i = 0;
//		String[] files = new String[n];
//		Iterator<String> it = keySet.iterator();
//		while(it.hasNext()) {
//			files[i++] = it.next();
//		}
//		return files;
//	}
//
//	/**
//	 * Returns maximum number of internally open RRD files
//	 * which still does not force garbage collector to run.
//	 *
//	 * @return Desired nuber of open files held in the pool.
//	 */
//	public synchronized int getCapacity() {
//		return capacity;
//	}
//
//	/**
//	 * Sets maximum number of internally open RRD files
//	 * which still does not force garbage collector to run.
//	 *
//	 * @param capacity Desired number of open files to hold in the pool
//	 */
//	public synchronized void setCapacity(int capacity) {
//		this.capacity = capacity;
////		debug("Capacity set to: " + capacity);
//	}
//
//	private RrdBackendFactory getFactory() throws RrdException {
//		if (factory == null) {
//			factory = RrdBackendFactory.getDefaultFactory();
//			if (!(factory instanceof RrdFileBackendFactory)) {
//				factory = null;
//				throw new RrdException(
//					"RrdDbPool cannot work with factories not derived from RrdFileBackendFactory");
//			}
//		}
//		return factory;
//	}
//
//	private class RrdEntry {
//		private RrdDb rrdDb;
//		private int usageCount = 1;
//
//		public RrdEntry(RrdDb rrdDb) {
//			this.rrdDb = rrdDb;
//		}
//
//		RrdDb getRrdDb() {
//			return rrdDb;
//		}
//
//		int reportUsage() {
//			assert usageCount >= 0: "Unexpected reportUsage count: " + usageCount;
//			return ++usageCount;
//		}
//
//		int reportRelease() {
//			assert usageCount > 0: "Unexpected reportRelease count: " + usageCount;
//			return --usageCount;
//		}
//
//		boolean isInUse() {
//			return usageCount > 0;
//		}
//
//		void closeRrdDb() throws IOException {
//			rrdDb.close();
//		}
//
//		String dump() throws IOException {
//			String canonicalPath = getCanonicalPath(rrdDb.getPath());
//			return canonicalPath + " [" + usageCount + "]";
//		}
//	}
//
//	/**
//	 * Calculates pool's efficency ratio. The ratio is obtained by dividing the number of
//	 * RrdDb requests served from the internal pool of open RRD files
//	 * with the number of total RrdDb requests.
//	 *
//	 * @return Pool's efficiency ratio as a double between 1 (best) and 0 (worst).
//	 * If no RrdDb reference was ever requested, 1 would be returned.
//	 */
//	public synchronized double getPoolEfficiency() {
//		if (poolRequestsCount == 0) {
//			return 1.0;
//		}
//		double ratio = (double) poolHitsCount / (double) poolRequestsCount;
//		// round to 3 decimal digits
//		return Math.round(ratio * 1000.0) / 1000.0;
//	}
//
//	/**
//	 * Returns the number of RRD requests served from the internal pool of open RRD files
//	 *
//	 * @return The number of pool "hits".
//	 */
//	public synchronized int getPoolHitsCount() {
//		return poolHitsCount;
//	}
//
//	/**
//	 * Returns the total number of RRD requests successfully served by this pool.
//	 *
//	 * @return Total number of RRD requests
//	 */
//	public synchronized int getPoolRequestsCount() {
//		return poolRequestsCount;
//	}
//
//	/**
//	 * Returns the maximum number of open RRD files over the lifetime
//	 * of the pool.
//	 * @return maximum number of open RRD files.
//	 */
//	public synchronized int getMaxUsedCapacity() {
//		return maxUsedCapacity;
//	}
//
//	/**
//	 * Checks the internal behaviour of the pool. See {@link #setLimitedCapacity(boolean)} for
//	 * more information.
//	 *
//	 * @return <code>true</code> if the pool is 'flexible' (by not imposing the strict
//	 * limit on the number of simultaneously open files), <code>false</code> otherwise.
//	 */
//	public synchronized boolean isLimitedCapacity() {
//		return limitedCapacity;
//	}
//
//	/**
//	 * Sets the behaviour of the pool. If <code>true</code> is passed as argument, the pool will never
//	 * open more than {@link #getCapacity()} files at any time. If set to <code>false</code>,
//	 * the pool might keep more open files, but only for a short period of time. This method might be
//	 * useful if you want avoid OS limits when it comes to the number of simultaneously open files.<p>
//	 *
//	 * By default, the pool behaviour is 'flexible' (<code>limitedCapacity</code> property defaults
//	 * to false<p>
//	 *
//	 * @param limitedCapacity <code>true</code> if the pool should be 'flexible' (not imposing the strict
//	 * limit on the number of simultaneously open files), <code>false</code> otherwise.
//	 */
//	public synchronized void setLimitedCapacity(boolean limitedCapacity) {
//		this.limitedCapacity = limitedCapacity;
//	}
//
//	private void proveActive() throws IOException {
//		if(!active) {
//			throw new IOException("RrdDbPool is already closed");
//		}
//	}
//
//	/**
//	 * Checks if the pool is active. You can request RrdDb references only from the active pool. The
//	 * pool is deactived when the {@link #close()} method is called.
//	 * @return <code>true</code> if active, <code>false</code> otherwise.
//	 */
//	public synchronized boolean isActive() {
//		return active;
//	}
//}
//


