package com.after_sunrise.oss.otdb.je.database.impl;
import static com.sleepycat.je.OperationStatus.SUCCESS;
import java.io.IOException;
import javax.annotation.Resource;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.after_sunrise.oss.otdb.je.database.CodeDatabase;
import com.after_sunrise.oss.otdb.je.database.Databases;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.sleepycat.bind.tuple.PackedLongBinding;
import com.sleepycat.bind.tuple.StringBinding;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.SecondaryConfig;
import com.sleepycat.je.SecondaryDatabase;
import com.sleepycat.je.Sequence;
import com.sleepycat.je.SequenceConfig;
import com.sleepycat.je.Transaction;
/**
* @author takanori.takase
*/
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CodeDatabaseImpl implements CodeDatabase {
private final Log log = LogFactory.getLog(getClass());
private final StringBinding keyBinding = new StringBinding();
private final PackedLongBinding valBinding = new PackedLongBinding();
@Value("DB_Codes")
private String mainDbName;
@Value("DB_CodesIndex")
private String secDbName;
@Value("DB_CodesSequences")
private String seqDbName;
@Value("SQ_Codes")
private String seqName;
@Resource(name = "codeMainDbConfig")
private DatabaseConfig mainDbConfig;
@Resource(name = "codeSecDbConfig")
private SecondaryConfig secDbConfig;
@Resource(name = "codeSeqDbConfig")
private DatabaseConfig seqDbConfig;
@Resource(name = "codeSequenceConfig")
private SequenceConfig seqConfig;
@Value("#{T(com.sleepycat.je.LockMode).${open-tickdb.code.lock}}")
private LockMode lockMode;
private Database database;
private SecondaryDatabase secDatabase;
private Database seqDatabase;
@Override
public void initialize(Environment environment, Transaction tx)
throws IOException {
database = environment.openDatabase(tx, mainDbName, mainDbConfig);
log.info("Initialized db : " + database.getDatabaseName());
secDatabase = environment.openSecondaryDatabase(tx, secDbName,
database, secDbConfig);
log.info("Initialized db : " + secDatabase.getDatabaseName());
seqDatabase = environment.openDatabase(tx, seqDbName, seqDbConfig);
log.info("Initialized db : " + seqDatabase.getDatabaseName());
}
@Override
public void close() throws IOException {
IOUtils.closeQuietly(seqDatabase);
IOUtils.closeQuietly(secDatabase);
IOUtils.closeQuietly(database);
}
@Override
public void sync() throws IOException {
Databases.sync(seqDatabase);
Databases.sync(secDatabase);
Databases.sync(database);
}
@VisibleForTesting
long generateSequenceId(Transaction txn) throws IOException {
DatabaseEntry key = new DatabaseEntry(seqName.getBytes(Charsets.UTF_8));
Sequence sequence = seqDatabase.openSequence(txn, key, seqConfig);
try {
return sequence.get(txn, 1);
} finally {
IOUtils.closeQuietly(sequence);
}
}
@Override
public Long search(Transaction tx, String code) throws IOException {
if (StringUtils.isBlank(code)) {
return null;
}
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry val = new DatabaseEntry();
keyBinding.objectToEntry(code, key);
if (SUCCESS != database.get(tx, key, val, lockMode)) {
return null;
}
return valBinding.entryToObject(val);
}
@Override
public String search(Transaction tx, long id) throws IOException {
DatabaseEntry secKey = new DatabaseEntry();
valBinding.objectToEntry(id, secKey);
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry val = new DatabaseEntry();
if (SUCCESS != secDatabase.get(tx, secKey, key, val, lockMode)) {
return null;
}
return keyBinding.entryToObject(key);
}
@Override
public long persist(Transaction tx, String code) throws IOException {
if (StringUtils.isBlank(code)) {
throw new IOException("Code cannot be blank.");
}
long id = generateSequenceId(tx);
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry val = new DatabaseEntry();
keyBinding.objectToEntry(code, key);
valBinding.objectToEntry(id, val);
if (SUCCESS != database.putNoOverwrite(tx, key, val)) {
throw new IOException("Duplicate code : " + code);
}
return id;
}
}