/*
* dCache - http://www.dcache.org/
*
* Copyright (C) 2016 Deutsches Elektronen-Synchrotron
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.pool.repository.meta.db;
import com.sleepycat.collections.StoredMap;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.EnvironmentFailureException;
import com.sleepycat.je.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Properties;
import diskCacheV111.util.CacheException;
import diskCacheV111.util.PnfsId;
import diskCacheV111.vehicles.StorageInfo;
import dmg.cells.nucleus.EnvironmentAware;
import org.dcache.pool.movers.IoMode;
import org.dcache.pool.repository.ReplicaStore;
import org.dcache.pool.repository.RepositoryChannel;
import org.dcache.util.ConfigurationMapFactoryBean;
/**
* Base class for BerkeleyDB backed ReplicaStore implementations.
*
* The database is stored in a subdirectory of the pool directory
* called 'meta'.
*
* The cache repository entries generated by this store fetch storage
* info from the database on demand.
*/
public abstract class AbstractBerkeleyDBReplicaStore implements ReplicaStore, EnvironmentAware
{
protected static final Logger LOGGER =
LoggerFactory.getLogger(AbstractBerkeleyDBReplicaStore.class);
protected static final String DIRECTORY_NAME = "meta";
protected final boolean readOnly;
/**
* Berkeley DB configuration properties.
*/
private final Properties properties = new Properties();
/**
* Directory containing the database.
*/
protected final Path dir;
/**
* The BerkeleyDB database to use.
*/
private ReplicaStoreDatabase database;
/**
* The BerkeleyDB database to use.
*/
protected ReplicaStoreView views;
public AbstractBerkeleyDBReplicaStore(Path directory, boolean readOnly) throws IOException
{
this.readOnly = readOnly;
dir = directory.resolve(DIRECTORY_NAME);
if (!Files.exists(dir)) {
Files.createDirectory(dir);
} else if (!Files.isDirectory(dir)) {
throw new FileNotFoundException("No such directory: " + dir);
}
}
@Override
public void setEnvironment(Map<String, Object> environment)
{
ConfigurationMapFactoryBean factory = new ConfigurationMapFactoryBean();
factory.setEnvironment(environment);
factory.setPrefix("pool.plugins.meta.db");
factory.buildMap();
properties.clear();
properties.putAll(factory.getObject());
}
@Override
public void init() throws CacheException
{
try {
database = new ReplicaStoreDatabase(properties, dir.toFile(), readOnly);
views = new ReplicaStoreView(database);
} catch (EnvironmentFailureException e) {
throw new CacheException(CacheException.PANIC, "Failed to open Berkeley DB database. When upgrading to " +
"dCache 2.6, it may be necessary to run the /usr/sbin/dcache-pool-meta-preupgrade utility " +
"before starting the pool. If that does not resolve the problem, you should contact " +
"support@dcache.org", e);
}
}
/**
* Returns a database backed map of all StorageInfo objects.
*/
StoredMap<String,StorageInfo> getStorageInfoMap()
{
return views.getStorageInfoMap();
}
/**
* Returns a database backed map of all state objects.
*/
StoredMap<String,CacheRepositoryEntryState> getStateMap()
{
return views.getStateMap();
}
/** Closes the database. */
@Override
public void close()
{
try {
database.close();
} catch (DatabaseException e) {
LOGGER.error("Ignored: Could not close database: " + e.getMessage());
}
}
@Override
public synchronized boolean isOk()
{
Path tmp = dir.resolve(".repository_is_ok");
try {
Files.deleteIfExists(tmp);
Files.createFile(tmp);
if (database.isFailed()) {
return false;
}
return true;
} catch (IOException e) {
LOGGER.error("Failed to touch " + tmp + ": " + e.getMessage());
return false;
}
}
public boolean isValid()
{
return database.getEnvironment().isValid();
}
public EnvironmentConfig getConfig()
{
return database.getEnvironment().getConfig();
}
public Transaction beginTransaction()
{
return database.getEnvironment().beginTransaction(null, null);
}
public abstract void setLastModifiedTime(PnfsId pnfsId, long time) throws IOException;
public abstract long getFileSize(PnfsId pnfsId) throws IOException;
public abstract URI getUri(PnfsId pnfsId);
public abstract RepositoryChannel openChannel(PnfsId pnfsId, IoMode mode) throws IOException;
}