package org.openntf.domino.tests.jpg;
import static java.lang.Math.pow;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.openntf.domino.Database;
import org.openntf.domino.DbDirectory;
import org.openntf.domino.Document;
import org.openntf.domino.Session;
/**
* @author jgallagher
*
*/
public class ShardingDatabase {
private final Session session_;
private final String server_;
private final HashingStrategy hashingStrategy_;
private final ServerStrategy serverStrategy_;
private final String baseName_;
private boolean recalcOnSave_;
private final Map<String, Database> dbCache_ = new HashMap<String, Database>();
private final Map<String, DbDirectory> dbdirCache_ = new HashMap<String, DbDirectory>();
private final int places_;
public ShardingDatabase(final Session session, final HashingStrategy hashingStrategy, final String server, final String baseName,
final int places) {
if (session == null)
throw new IllegalArgumentException("session cannot be null");
if (baseName == null || baseName.length() == 0)
throw new IllegalArgumentException("baseName cannot be null or zero-length");
if (places < 0)
throw new IllegalArgumentException("databases must be nonnegative");
session_ = session;
baseName_ = baseName.toLowerCase().endsWith(".nsf") ? baseName.substring(0, baseName.length() - 4) : baseName;
places_ = places;
hashingStrategy_ = hashingStrategy;
server_ = server == null ? "" : server;
dbdirCache_.put("only", session_.getDbDirectory(server_));
serverStrategy_ = null;
}
public ShardingDatabase(final Session session, final HashingStrategy hashingStrategy, final ServerStrategy serverStrategy,
final String baseName, final int places) {
if (session == null)
throw new IllegalArgumentException("session cannot be null");
if (baseName == null || baseName.length() == 0)
throw new IllegalArgumentException("baseName cannot be null or zero-length");
if (places < 1)
throw new IllegalArgumentException("places must be greater than zero");
if (serverStrategy == null)
throw new IllegalArgumentException("serverStrategy cannot be null");
session_ = session;
baseName_ = baseName.toLowerCase().endsWith(".nsf") ? baseName.substring(0, baseName.length() - 4) : baseName;
places_ = places;
hashingStrategy_ = hashingStrategy;
server_ = null;
serverStrategy_ = serverStrategy;
}
public void initializeDatabases() {
if (places_ > 0) {
for (int i = 0; i < pow(16, places_); i++) {
String hashChunk = Integer.toString(i, 16).toLowerCase();
while (hashChunk.length() < places_)
hashChunk = "0" + hashChunk;
String server = serverStrategy_ == null ? server_ : serverStrategy_.getServerForHashChunk(hashChunk);
DbDirectory dbdir = getDbDirectoryForHashChunk(hashChunk);
String dbName = baseName_ + "-" + hashChunk + ".nsf";
Database database = session_.getDatabase(server, dbName, true);
if (!database.isOpen()) {
database = createDatabase(dbdir, dbName);
}
dbCache_.put(dbName, database);
}
} else {
Database database = session_.getDatabase(server_, baseName_ + ".nsf");
if (!database.isOpen()) {
database = createDatabase(getDbDirectoryForHashChunk(null), baseName_ + ".nsf");
}
dbCache_.put(baseName_ + ".nsf", database);
}
}
public ShardingDocument createDocument() {
return new ShardingDocument(getStagingDatabase().createDocument());
}
public ShardingDocument getDocumentByUNID(final String unid) {
return new ShardingDocument(getDatabaseForHash(unid).getDocumentByUNID(unid));
}
private Database getStagingDatabase() {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < places_; i++) {
builder.append('0');
}
String hashChunk = builder.toString();
String dbName = baseName_ + "-" + hashChunk + ".nsf";
if (!dbCache_.containsKey(dbName)) {
String server = serverStrategy_ == null ? server_ : serverStrategy_.getServerForHashChunk(hashChunk);
Database database = session_.getDatabase(server, dbName, true);
if (!database.isOpen()) {
DbDirectory dbdir = getDbDirectoryForHashChunk(hashChunk);
database = createDatabase(dbdir, dbName);
}
dbCache_.put(dbName, database);
}
return dbCache_.get(dbName);
}
private Database getDatabaseForHash(final String hash) {
String hashChunk = hash.substring(0, places_);
String dbName = baseName_ + "-" + hashChunk + ".nsf";
if (!dbCache_.containsKey(dbName)) {
String server = serverStrategy_ == null ? server_ : serverStrategy_.getServerForHashChunk(hashChunk);
Database database = session_.getDatabase(server, dbName, true);
if (!database.isOpen()) {
DbDirectory dbdir = getDbDirectoryForHashChunk(hashChunk);
database = createDatabase(dbdir, dbName);
}
dbCache_.put(dbName, database);
}
return dbCache_.get(dbName);
}
private DbDirectory getDbDirectoryForHashChunk(final String hashChunk) {
if (server_ != null) {
return dbdirCache_.get("only");
} else {
if (!dbdirCache_.containsKey(hashChunk)) {
String server = serverStrategy_ == null ? server_ : serverStrategy_.getServerForHashChunk(hashChunk);
dbdirCache_.put(hashChunk, session_.getDbDirectory(server));
}
return dbdirCache_.get(hashChunk);
}
}
private Database createDatabase(final DbDirectory dbdir, final String dbName) {
Database database = dbdir.createDatabase(dbName, true);
database.createView();
database.setOption(Database.DBOption.LZ1, true);
database.setOption(Database.DBOption.COMPRESSDESIGN, true);
database.setOption(Database.DBOption.COMPRESSDOCUMENTS, true);
database.setOption(Database.DBOption.NOUNREAD, true);
return database;
}
public interface ServerStrategy {
String getServerForHashChunk(final String hashChunk);
}
public interface HashingStrategy {
String getHashForMap(final Map<String, Object> doc);
}
public class ShardingDocument {
private Document doc_;
protected ShardingDocument(final Document origDoc) {
doc_ = origDoc;
}
public Object getItemValue(final String itemName) {
return doc_.getItemValue(itemName);
}
public void replaceItemValue(final String itemName, final Object value) {
doc_.replaceItemValue(itemName, value);
}
public Document getDoc() {
return doc_;
}
public boolean save() {
Database destDB;
if (doc_.isNewNote() && hashingStrategy_ != null) {
String hash = hashingStrategy_.getHashForMap(doc_);
doc_.setUniversalID(hash);
doc_.replaceItemValue("$Created", new Date());
destDB = getDatabaseForHash(hash);
} else {
destDB = getDatabaseForHash(doc_.getUniversalID());
}
Database currentDB = doc_.getParentDatabase();
if (!(currentDB.getFilePath().equalsIgnoreCase(destDB.getFilePath()) && currentDB.getServer().equalsIgnoreCase(
destDB.getServer()))) {
Document destDoc = destDB.createDocument();
doc_.copyAllItems(destDoc, true);
destDoc.replaceItemValue("$Created", doc_.getCreated());
destDoc.setUniversalID(doc_.getUniversalID());
doc_ = destDoc;
}
return doc_.save();
}
}
public boolean isRecalcOnSave() {
return recalcOnSave_;
}
public void setRecalcOnSave(final boolean recalcOnSave) {
this.recalcOnSave_ = recalcOnSave;
}
}