All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datacleaner.storage.BerkeleyDbStorageProvider Maven / Gradle / Ivy

There is a newer version: 6.0.0
Show newest version
/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Neopost - Customer Information Management
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program 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 distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.datacleaner.storage;

import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Type;
import java.util.UUID;

import org.datacleaner.descriptors.ProvidedPropertyDescriptor;
import org.datacleaner.util.ReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sleepycat.bind.ByteArrayBinding;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.tuple.BooleanBinding;
import com.sleepycat.bind.tuple.ByteBinding;
import com.sleepycat.bind.tuple.CharacterBinding;
import com.sleepycat.bind.tuple.DoubleBinding;
import com.sleepycat.bind.tuple.FloatBinding;
import com.sleepycat.bind.tuple.IntegerBinding;
import com.sleepycat.bind.tuple.LongBinding;
import com.sleepycat.bind.tuple.ShortBinding;
import com.sleepycat.bind.tuple.StringBinding;
import com.sleepycat.collections.StoredKeySet;
import com.sleepycat.collections.StoredMap;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;

/**
 * Berkeley DB based implementation of the {@link StorageProvider} interface.
 * 
 * 
 */
public final class BerkeleyDbStorageProvider implements StorageProvider {

	private static final String DIRECTORY_PREFIX = "analyzerBeans_";
	private final Logger logger = LoggerFactory.getLogger(getClass());
	private final File _parentDirectory;
	private File _targetDir;
	private Environment _environment;
	private boolean _deleteOnExit = false;

	public BerkeleyDbStorageProvider(File parentDirectory) {
		if (!parentDirectory.exists()) {
			if (!parentDirectory.mkdirs()) {
				throw new IllegalArgumentException(
						"Could not create directory: " + parentDirectory);
			}
		}
		_parentDirectory = parentDirectory;
	}

	public File getParentDirectory() {
		return _parentDirectory;
	}

	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		if (_environment != null) {
			_environment.close();
			cleanDirectory();
		}
	}

	/**
	 * Cleans the parent directory of this storage provider. This action will
	 * delete all previous collection storages made in this directory, and thus
	 * it should only be invoked either before any collections has been made or
	 * when all collections are ensured to be unused.
	 */
	public void cleanDirectory() {
		File[] directories = _parentDirectory.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.startsWith(DIRECTORY_PREFIX);
			}
		});
		for (File directory : directories) {
			delete(directory);
		}
	}

	/**
	 * Recursively deletes a directory and all it's files
	 * 
	 * @param file
	 */
	private void delete(File file) {
		if (file.isDirectory()) {
			File[] children = file.listFiles();
			for (File child : children) {
				delete(child);
			}
		}
		if (!file.delete()) {
			if (!file.isDirectory()) {
				logger.warn("Unable to clean/delete file: {}", file);
			} else {
				logger.debug("Unable to clean/delete directory: {}", file);
			}
		}
	}

	public Object createProvidedCollection(
			ProvidedPropertyDescriptor providedDescriptor) {
		Type typeArgument = providedDescriptor.getTypeArgument(0);
		Class clazz1 = (Class) typeArgument;
		if (providedDescriptor.isList()) {
			return createList(clazz1);
		} else if (providedDescriptor.isSet()) {
			return createSet(clazz1);
		} else if (providedDescriptor.isMap()) {
			Class clazz2 = (Class) providedDescriptor.getTypeArgument(1);
			return createMap(clazz1, clazz2);
		} else {
			// This should never happen (is checked by the
			// ProvidedDescriptor)
			throw new IllegalStateException();
		}
	}

	protected Environment getEnvironment() throws DatabaseException {
		if (_environment == null) {
			EnvironmentConfig config = new EnvironmentConfig();
			config.setAllowCreate(true);
			File targetDir = getTargetDir();
			_environment = new Environment(targetDir, config);
		}
		return _environment;
	}

	private File getTargetDir() {
		if (_targetDir == null) {
			while (_targetDir == null) {
				try {
					File candidateDir = new File(_parentDirectory,
							DIRECTORY_PREFIX + UUID.randomUUID().toString());
					if (!candidateDir.exists() && candidateDir.mkdir()) {
						_targetDir = candidateDir;
						_deleteOnExit = true;
					}
				} catch (Exception e) {
					logger.error(
							"Exception thrown while trying to create targetDir inside tempDir",
							e);
					_targetDir = _parentDirectory;
				}
			}
			if (logger.isInfoEnabled()) {
				logger.info("Using target directory for persistent collections (deleteOnExit="
						+ _deleteOnExit + "): " + _targetDir.getAbsolutePath());
			}
			initDeleteOnExit(_targetDir);
		}
		return _targetDir;
	}

	private void initDeleteOnExit(File dir) {
		File[] files = dir.listFiles();
		dir.deleteOnExit();
		for (File file : files) {
			if (file.isDirectory()) {
				initDeleteOnExit(file);
			} else if (file.isFile()) {
				file.deleteOnExit();
			} else {
				logger.warn("Unable to set the deleteOnExit flag on file: "
						+ file);
			}
		}
	}

	private Database createDatabase() throws DatabaseException {
		DatabaseConfig databaseConfig = new DatabaseConfig();
		databaseConfig.setAllowCreate(true);
		String databaseName = UUID.randomUUID().toString();
		Database database = getEnvironment().openDatabase(null, databaseName,
				databaseConfig);
		return database;
	}

	@Override
	public  BerkeleyDbList createList(Class valueType)
			throws IllegalStateException {
		BerkeleyDbMap map = createMap(Integer.class, valueType);

		// Berkeley StoredLists are non-functional!
		// return new StoredList(createDatabase(), valueBinding, true);

		return new BerkeleyDbList(map);
	}

	@Override
	public  BerkeleyDbSet createSet(Class valueType)
			throws IllegalStateException {
		try {
			Database database = createDatabase();
			StoredKeySet set = new StoredKeySet(database,
					createBinding(valueType), true);
			return new BerkeleyDbSet(getEnvironment(), database, set);
		} catch (DatabaseException e) {
			throw new IllegalStateException(e);
		}
	}

	@Override
	public  BerkeleyDbMap createMap(Class keyType,
			Class valueType) throws IllegalStateException {
		try {
			final EntryBinding keyBinding = createBinding(keyType);
			final EntryBinding valueBinding = createBinding(valueType);
			final Database database = createDatabase();
			final StoredMap map = new StoredMap(database, keyBinding,
					valueBinding, true);
			return new BerkeleyDbMap(getEnvironment(), database, map);
		} catch (DatabaseException e) {
			throw new IllegalStateException(e);
		}
	}

	private EntryBinding createBinding(Type type)
			throws UnsupportedOperationException {
		if (ReflectionUtils.isString(type)) {
			return new StringBinding();
		}
		if (ReflectionUtils.isInteger(type)) {
			return new IntegerBinding();
		}
		if (ReflectionUtils.isLong(type)) {
			return new LongBinding();
		}
		if (ReflectionUtils.isBoolean(type)) {
			return new BooleanBinding();
		}
		if (ReflectionUtils.isShort(type)) {
			return new ShortBinding();
		}
		if (ReflectionUtils.isByte(type)) {
			return new ByteBinding();
		}
		if (ReflectionUtils.isDouble(type)) {
			return new DoubleBinding();
		}
		if (ReflectionUtils.isFloat(type)) {
			return new FloatBinding();
		}
		if (ReflectionUtils.isCharacter(type)) {
			return new CharacterBinding();
		}
		if (ReflectionUtils.isByteArray(type)) {
			return new ByteArrayBinding();
		}
		throw new UnsupportedOperationException(
				"Cannot provide collection of type " + type);
	}

	@Override
	public RowAnnotationFactory createRowAnnotationFactory() {
		// TODO: Create a persistent RowAnnotationFactory
		return new InMemoryRowAnnotationFactory2();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy