package com.after_sunrise.oss.otdb.je.database.impl; import static com.sleepycat.je.OperationStatus.SUCCESS; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.annotation.Resource; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; 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.binding.TickKeyTupleBinding; import com.after_sunrise.oss.otdb.je.binding.TickValueTupleBinding; import com.after_sunrise.oss.otdb.je.database.Databases; import com.after_sunrise.oss.otdb.je.database.ForwardIterator; import com.after_sunrise.oss.otdb.je.database.TickDatabase; import com.after_sunrise.oss.otdb.je.database.TickDatabaseIterator; import com.after_sunrise.oss.otdb.je.entity.TickEntry; import com.after_sunrise.oss.otdb.je.entity.TickKey; import com.after_sunrise.oss.otdb.je.entity.TickValue; import com.google.common.base.Charsets; import com.sleepycat.je.Cursor; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseConfig; import com.sleepycat.je.DatabaseEntry; import com.sleepycat.je.DiskOrderedCursor; import com.sleepycat.je.DiskOrderedCursorConfig; import com.sleepycat.je.Environment; import com.sleepycat.je.LockMode; import com.sleepycat.je.OperationStatus; 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 TickDatabaseImpl implements TickDatabase { private final Log log = LogFactory.getLog(getClass()); @Value("DB_Ticks") private String mainDbName; @Value("DB_TickSequences") private String seqDbName; @Value("SQ_Ticks") private String seqName; @Resource(name = "tickMainDbConfig") private DatabaseConfig mainDbConfig; @Resource(name = "tickSeqDbConfig") private DatabaseConfig seqDbConfig; @Resource(name = "tickSequenceConfig") private SequenceConfig seqConfig; @Value("#{T(com.sleepycat.je.LockMode).${open-tickdb.tick.lock}}") private LockMode lockMode; @Autowired private DiskOrderedCursorConfig cursorConfig; @Autowired private TickKeyTupleBinding keyBinding; @Autowired private TickValueTupleBinding valBinding; private Database database; private Database seqDatabase; @Override public void initialize(Environment env, Transaction tx) throws IOException { database = env.openDatabase(tx, mainDbName, mainDbConfig); log.info("Initialized db : " + database.getDatabaseName()); seqDatabase = env.openDatabase(tx, seqDbName, seqDbConfig); log.info("Initialized db : " + seqDatabase.getDatabaseName()); } @Override public void close() throws IOException { IOUtils.closeQuietly(seqDatabase); IOUtils.closeQuietly(database); } @Override public void sync() throws IOException { Databases.sync(seqDatabase); Databases.sync(database); } @Override public long count() { long count = 0L; try (DiskOrderedCursor cursor = database.openCursor(cursorConfig)) { DatabaseEntry key = new DatabaseEntry(); DatabaseEntry val = new DatabaseEntry(); while (cursor.getNext(key, val, null) == SUCCESS) { count++; } } return count; } @Override public List<TickEntry> find(Transaction tx, long id, Long start, Long end) throws IOException { ArrayList<TickEntry> ticks = new ArrayList<>(); try (TickDatabaseIterator itr = iterator(tx, id, start, end)) { TickEntry tick = itr.first(); while (tick != null) { ticks.add(tick); tick = itr.next(); } } ticks.trimToSize(); return ticks; } @Override public TickDatabaseIterator iterator(Transaction tx, long id, Long start, Long end) throws IOException { Cursor cursor = database.openCursor(tx, null); long s = start == null ? Long.MIN_VALUE : start; long e = end == null ? Long.MAX_VALUE : end; return new TickDatabaseIteratorImpl(cursor, lockMode, keyBinding, valBinding, id, s, e); } @Override public ForwardIterator<TickKey> keyIterator(Transaction tx) throws IOException { final DiskOrderedCursor cursor = database.openCursor(cursorConfig); final DatabaseEntry key = new DatabaseEntry(); final DatabaseEntry val = new DatabaseEntry(); return new ForwardIterator<TickKey>() { @Override public void close() throws IOException { cursor.close(); } @Override public TickKey next() { if (SUCCESS != cursor.getNext(key, val, null)) { return null; } return keyBinding.entryToObject(key); } }; } @Override public ForwardIterator<TickEntry> entryIterator(Transaction tx) throws IOException { final Cursor cursor = database.openCursor(tx, null); final DatabaseEntry key = new DatabaseEntry(); final DatabaseEntry val = new DatabaseEntry(); return new ForwardIterator<TickEntry>() { @Override public void close() throws IOException { cursor.close(); } @Override public TickEntry next() { if (SUCCESS != cursor.getNext(key, val, lockMode)) { return null; } TickKey k = keyBinding.entryToObject(key); TickValue v = valBinding.entryToObject(val); return new TickEntry(k, v); } }; } @Override public long generateSequenceId(Transaction txn) throws IOException { return generateSequenceId(txn, 1); } @Override public long generateSequenceId(Transaction txn, int delta) throws IOException { DatabaseEntry key = new DatabaseEntry(seqName.getBytes(Charsets.UTF_8)); Sequence sequence = seqDatabase.openSequence(txn, key, seqConfig); try { return sequence.get(txn, delta); } finally { IOUtils.closeQuietly(sequence); } } @Override public void persist(Transaction tx, TickEntry tick) throws IOException { persist(tx, tick, false); } @Override public void persist(Transaction tx, TickEntry tick, boolean overwrite) throws IOException { if (tick == null) { throw new IOException("Cannot persist null."); } DatabaseEntry key = new DatabaseEntry(); keyBinding.objectToEntry(tick.getKey(), key); DatabaseEntry val = new DatabaseEntry(); valBinding.objectToEntry(tick.getValue(), val); OperationStatus status; if (overwrite) { status = database.put(tx, key, val); } else { status = database.putNoOverwrite(tx, key, val); } if (SUCCESS == status) { return; } throw new IOException("Failed to persist tick : " + tick); } @Override public boolean delete(Transaction tx, TickKey key) throws IOException { if (key == null) { return false; } DatabaseEntry entry = new DatabaseEntry(); keyBinding.objectToEntry(key, entry); return database.delete(tx, entry) == SUCCESS; } }