/**
* HiveDB is an Open Source (LGPL) system for creating large, high-transaction-volume
* data storage systems.
*/
package org.hivedb;
import org.hivedb.meta.*;
import org.hivedb.meta.directory.*;
import org.hivedb.meta.persistence.*;
import org.hivedb.util.HiveUtils;
import org.hivedb.util.Preconditions;
import org.hivedb.util.database.DriverLoader;
import org.hivedb.util.database.HiveDbDialect;
import org.hivedb.util.database.Schemas;
import org.hivedb.util.functional.Atom;
import org.hivedb.util.functional.Filter;
import org.hivedb.util.functional.Predicate;
import javax.sql.DataSource;
import java.util.*;
/**
* @author Kevin Kelm (kkelm@fortress-consulting.com)
* @author Andy Likuski (alikuski@cafepress.com)
* @author Britt Crawford (bcrawford@cafepress.com)
* <p/>
* The facade for all fundamental CRUD operations on the Hive directories and Hive metadata.
*/
public class Hive extends Observable implements Synchronizeable, Observer, Lockable, Nameable {
//constants
private static final int DEFAULT_JDBC_TIMEOUT = 500;
public static final int NEW_OBJECT_ID = 0;
private HiveSemaphore semaphore;
private String hiveUri;
private ConnectionManager connection;
private PartitionDimension dimension;
private DirectoryFacade directory;
private Collection<Node> nodes = new ArrayList<Node>();
private DirectoryFacadeProvider directoryFacadeProvider;
private DataSource hiveDataSource;
private HiveDataSourceProvider dataSourceProvider;
private Assigner assigner = new RandomAssigner();
public Hive(String hiveUri, int i, Status writable, HiveDataSourceProvider provider, DirectoryFacadeProvider directoryFacadeProvider) {
this(hiveUri, i, writable, provider);
this.directoryFacadeProvider = directoryFacadeProvider;
}
/**
* Calls {@see #load(String,DataSourceProvider,Assigner)} with the
* default DataSourceProvider.
*/
public static Hive load(String hiveDatabaseUri, HiveDataSourceProvider dataSourceProvider) {
return load(hiveDatabaseUri, dataSourceProvider, new RandomAssigner());
}
public static Hive load(String hiveDatabaseUri, HiveDataSourceProvider dataSourceProvider, Assigner assigner, DirectoryFacadeProvider directoryFacadeProvider) {
Hive hive = prepareHive(hiveDatabaseUri, dataSourceProvider, assigner, directoryFacadeProvider);
hive.sync();
return hive;
}
/**
* Loads an existing hive at the given location using the given DataSourceProvider and
* default Assigner. <b>Side Effect:</b> creates a HiveSemaphore that keeps the Hive
* instance synchronized with the hive database metadata. {@see org.hivedb.meta.HiveSemaphore#}
*
* @param hiveUri The URI of an existing hive configuration.
* @param provider The DataSourceProvider used to resolve the DataSource of a data node.
* @param assigner The key assigner to be used by the Hive for identifying new unidentified entity instances
* @return a Hive instance
*/
//TODO add DirectoryFacadeProvider
public static Hive load(String hiveUri, HiveDataSourceProvider provider, Assigner assigner) {
DirectoryFacadeProvider directoryFacadeProvider = getDirectoryFacadeProvider();
return load(hiveUri, provider, assigner, directoryFacadeProvider);
}
private static DirectoryFacadeProvider getDirectoryFacadeProvider() {
DirectoryProvider directoryProvider = new DbDirectoryFactory(CachingDataSourceProvider.getInstance());
DirectoryFacadeProvider directoryFacadeProvider = new DirectoryWrapperFactory(directoryProvider, CachingDataSourceProvider.getInstance());
return directoryFacadeProvider;
}
/**
* Calls {@see #create(String,String,int,DataSourceProvider,Assigner)} with the default
* DataSourceProvider.
*/
public static Hive create(String hiveUri, String dimensionName, int indexType, HiveDataSourceProvider provider) {
return create(hiveUri, dimensionName, indexType, provider, null);
}
/**
* Creates a new Hive. Primary caller: {@see org.hivedb.hibernate.ConfigurationReader#install(Hive)}
*
* @param hiveUri - The location of the hive database
* @param dimensionName - The name of the hive's partitioning dimension, used for naming hive index tables.
* @param indexType - The ___ indicating the type of the primary index of the partion dimension.
* @param provider - The DataSourceProvider used to resolve the DataSource of a data node.
* @param assigner - The key assigner to be used by the Hive for identifying new unidentified entity instances.
* @return an instance to access the created hive.
*/
public static Hive create(String hiveUri, String dimensionName, int indexType, HiveDataSourceProvider provider, Assigner assigner) {
Hive hive = prepareHive(hiveUri, provider, assigner, getDirectoryFacadeProvider());
PartitionDimension dimension = new PartitionDimension(dimensionName, indexType);
dimension.setIndexUri(hiveUri);
DataSource ds = provider.getDataSource(hiveUri);
PartitionDimensionDao dao = new PartitionDimensionDao(ds);
final List<PartitionDimension> partitionDimensions = dao.loadAll();
if (partitionDimensions.size() == 0) {
dao.create(dimension);
Schemas.install(new IndexSchema(dimension), dimension.getIndexUri());
hive.incrementAndPersistHive(ds);
return hive;
} else
throw new HiveRuntimeException(String.format("There is already a Hive with a partition dimension named %s intalled at this uri: %s", Atom.getFirstOrThrow(partitionDimensions).getName(), hiveUri));
}
private static Hive prepareHive(String hiveUri, HiveDataSourceProvider provider, Assigner assigner, DirectoryFacadeProvider directoryFacadeProvider) {
DriverLoader.initializeDriver(hiveUri);
Hive hive = new Hive(hiveUri, 0, Status.writable, provider, directoryFacadeProvider);
if (assigner != null)
hive.setAssigner(assigner);
return hive;
}
/**
* Used internally to synchronize the loaded Hive instance to the current state of the Hive database metadata.
*/
public boolean sync() {
boolean updated = false;
HiveSemaphore hs = new HiveSemaphoreDao(hiveDataSource).get();
if (this.getRevision() != hs.getRevision()) {
this.setSemaphore(hs);
initialize(hiveDataSource);
updated = true;
}
return updated;
}
/**
* {@see #sync()}
*
* @return
*/
public boolean forceSync() {
initialize(hiveDataSource);
return true;
}
private void initialize(DataSource ds) {
//Always fetch and add nodes
Collection<Node> nodes = new NodeDao(hiveDataSource).loadAll();
synchronized (this) {
this.nodes = nodes;
}
//Only synchronize other properties if a Partition Dimension exists
try {
PartitionDimension dimension = new PartitionDimensionDao(ds).get();
DirectoryFacade directory = directoryFacadeProvider.getDirectoryFacade(hiveUri, getAssigner(), getSemaphore(), dimension);
synchronized (this) {
ConnectionManager connection = new ConnectionManager(directory, this, dataSourceProvider);
this.dimension = dimension;
this.directory = directory;
this.connection = connection;
}
} catch (HiveRuntimeException e) {
//quash
}
}
protected Hive() {
this.semaphore = new HiveSemaphoreImpl();
}
protected Hive(String hiveUri, int revision, Status status, HiveDataSourceProvider dataSourceProvider) {
this();
this.hiveUri = hiveUri;
this.semaphore.setRevision(revision);
this.semaphore.setStatus(status);
this.dataSourceProvider = dataSourceProvider;
this.hiveDataSource = dataSourceProvider.getDataSource(hiveUri);
}
/**
* {@inheritDoc}
*/
public String getUri() {
return hiveUri;
}
/**
* {@inheritDoc}
*/
public DataSourceProvider getDataSourceProvider() {
return dataSourceProvider;
}
/**
* Sets the dataSourceProvider for testing purposes. Use {@see #load(String,DataSourceProvider,Assigner)} instead.
*
* @param dataSourceProvider
*/
public void setDataSourceProvider(HiveDataSourceProvider dataSourceProvider) {
this.dataSourceProvider = dataSourceProvider;
}
/**
* Hashes the Hive based on hiveUri, revision, partition dimension
*/
public int hashCode() {
return HiveUtils.makeHashCode(new Object[]{hiveUri, getRevision(), dimension});
}
/**
* Tests equality with {@see #hashCode()}
*/
public boolean equals(Object obj) {
return hashCode() == obj.hashCode();
}
public Status getStatus() {
return semaphore.getStatus();
}
/**
* Updates the Hive to new lockable status
*
* @param status
*/
public void updateHiveStatus(Status status) {
this.semaphore.setStatus(status);
new HiveSemaphoreDao(hiveDataSource).update(this.semaphore);
}
/**
* {@inheritDoc}
*/
public int getRevision() {
return semaphore.getRevision();
}
/**
* {@inheritDoc}
*/
public HiveSemaphore getSemaphore() {
return semaphore;
}
public HiveSemaphore setSemaphore(HiveSemaphore semaphore) {
this.semaphore = semaphore;
return semaphore;
}
/**
* {@inheritDoc}
*/
public PartitionDimension getPartitionDimension() {
return this.dimension;
}
/**
* @param dimension
* @return
*/
public PartitionDimension setPartitionDimension(PartitionDimension dimension) {
this.dimension = dimension;
incrementAndPersistHive(hiveDataSource);
sync();
return getPartitionDimension();
}
private void incrementAndPersistHive(DataSource datasource) {
new HiveSemaphoreDao(datasource).incrementAndPersist();
this.sync();
}
/**
* Gets the ConnectionManager, probably only useful for testing.
*
* @return
*/
public ConnectionManager connection() {
return this.connection;
}
/**
* e
* Dumps the properties of the Hive.
*/
public String toString() {
return HiveUtils.toDeepFormatedString(this, "HiveUri", getUri(),
"Revision", getRevision(), "PartitionDimensions",
getPartitionDimension());
}
/**
* {@inheritDoc}
*/
public HiveDbDialect getDialect() {
return DriverLoader.discernDialect(hiveUri);
}
/**
* {@inheritDoc}
*/
public void update(Observable o, Object arg) {
if (sync())
notifyObservers();
}
/**
* {@inheritDoc}
*/
public void notifyObservers() {
super.setChanged();
super.notifyObservers();
}
/**
* {@inheritDoc}
*/
public Assigner getAssigner() {
return assigner;
}
/**
* Sets the assigner. Use {@see #load(String,DataSourceProvider,Assigner)} instead.
*
* @param assigner
*/
public void setAssigner(Assigner assigner) {
this.assigner = assigner;
}
/**
* Exposes the DirectoryFacade for hive index operations. See {@see org.hivedb.meta.directory.DirectoryFacade}
*
* @return
*/
public DirectoryFacade directory() {
return this.directory;
}
//Configuration functions
public void updateNodeStatus(Node node, Status status) {
node.setStatus(status);
try {
this.updateNode(node);
} catch (HiveLockableException e) {
//quash since the hive is already read-only
}
}
/**
* {@inheritDoc}
*/
public Node addNode(Node node)
throws HiveLockableException {
Preconditions.isWritable(this);
Preconditions.nameIsUnique(getNodes(), node);
NodeDao nodeDao = new NodeDao(hiveDataSource);
nodeDao.create(node);
incrementAndPersistHive(hiveDataSource);
return node;
}
/**
* {@inheritDoc}
*/
public Collection<Node> addNodes(Collection<Node> nodes) throws HiveLockableException {
Preconditions.isWritable(this);
for (Node node : nodes) {
Preconditions.nameIsUnique(getNodes(), node);
NodeDao nodeDao = new NodeDao(hiveDataSource);
nodeDao.create(node);
}
incrementAndPersistHive(hiveDataSource);
return nodes;
}
/**
* {@inheritDoc}
*/
public boolean doesResourceExist(String resourceName) {
return !Preconditions.isNameUnique(dimension.getResources(), resourceName);
}
/**
* {@inheritDoc}
*/
public Resource addResource(Resource resource) throws HiveLockableException {
resource.setPartitionDimension(dimension);
Preconditions.isWritable(this);
Preconditions.isNameUnique(dimension.getResources(), resource.getName());
ResourceDao resourceDao = new ResourceDao(hiveDataSource);
resourceDao.create(resource);
incrementAndPersistHive(hiveDataSource);
Schemas.install(dimension);
return dimension.getResource(resource.getName());
}
/**
* {@inheritDoc}
*/
public SecondaryIndex addSecondaryIndex(Resource resource, SecondaryIndex secondaryIndex) throws HiveLockableException {
secondaryIndex.setResource(resource);
Preconditions.isWritable(this);
Preconditions.nameIsUnique(resource.getSecondaryIndexes(), secondaryIndex);
SecondaryIndexDao secondaryIndexDao = new SecondaryIndexDao(hiveDataSource);
secondaryIndexDao.create(secondaryIndex);
incrementAndPersistHive(hiveDataSource);
Schemas.install(dimension);
return secondaryIndex;
}
/**
* Updates the Hive's PartitionDimension to the given instance. The hive partition_dimension_metadata
* table is updated immediately to reflect any changes. Note that changes to nodes are not
* updated by this call and must be updated explicitly. See {@see #updateNod(Node)}.
* This method increments the hive version.
*
* @param partitionDimension
* @return the given PartitionDimension
* @throws HiveLockableException
*/
public PartitionDimension updatePartitionDimension(PartitionDimension partitionDimension) throws HiveLockableException {
Preconditions.isWritable(this);
PartitionDimensionDao partitionDimensionDao = new PartitionDimensionDao(hiveDataSource);
partitionDimensionDao.update(partitionDimension);
incrementAndPersistHive(hiveDataSource);
return partitionDimension;
}
/**
* Updates the Node of the hive matching the id of the given Node to the given instance.
* The hive node_metadata table is updated immediately to reflect any changes, and the hive version is incremented.
*
* @return the given PartitionDimension
* @throws HiveLockableException
*/
public Node updateNode(Node node) throws HiveLockableException {
Preconditions.isWritable(this);
Preconditions.idIsPresentInList(getNodes(), node);
new NodeDao(hiveDataSource).update(node);
incrementAndPersistHive(hiveDataSource);
return node;
}
/**
* {@inheritDoc}
*/
public Node deleteNode(Node node) throws HiveLockableException {
Preconditions.isWritable(this);
Preconditions.idIsPresentInList(getNodes(), node);
NodeDao nodeDao = new NodeDao(hiveDataSource);
nodeDao.delete(node);
incrementAndPersistHive(hiveDataSource);
//Synchronize the DataSourceCache
connection.removeNode(node);
return node;
}
/**
* {@inheritDoc}
*/
public Resource deleteResource(Resource resource) throws HiveLockableException {
Preconditions.isWritable(this);
Preconditions.idIsPresentInList(this.getPartitionDimension().getResources(), resource);
ResourceDao resourceDao = new ResourceDao(hiveDataSource);
resourceDao.delete(resource);
incrementAndPersistHive(hiveDataSource);
return resource;
}
/**
* {@inheritDoc}
*/
public SecondaryIndex deleteSecondaryIndex(SecondaryIndex secondaryIndex) throws HiveLockableException {
Preconditions.isWritable(this);
Preconditions.idIsPresentInList(secondaryIndex.getResource().getSecondaryIndexes(), secondaryIndex);
SecondaryIndexDao secondaryindexDao = new SecondaryIndexDao(hiveDataSource);
secondaryindexDao.delete(secondaryIndex);
incrementAndPersistHive(hiveDataSource);
return secondaryIndex;
}
/**
* {@inheritDoc}
*/
public Collection<Node> getNodes() {
return nodes;
}
/**
* {@inheritDoc}
*/
public Node getNode(final String name) {
return Filter.grepSingle(new Predicate<Node>() {
public boolean f(Node item) {
return item.getName().equalsIgnoreCase(name);
}
}, getNodes());
}
/**
* Returns the node with the given id
*
* @param id
* @return
*/
public Node getNode(final int id) {
return Filter.grepSingle(new Predicate<Node>() {
public boolean f(Node item) {
return item.getId() == id;
}
}, getNodes());
}
/**
* returns the name of the hive, "hive"
*/
public String getName() {
return "hive";
}
}