package database; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NavigableSet; import java.util.Observable; import java.util.Observer; import java.util.TreeMap; import ntp.NTP; import org.mapdb.BTreeKeySerializer; import org.mapdb.DB; import org.mapdb.Fun; import org.mapdb.Fun.Tuple2; import org.mapdb.Fun.Tuple2Comparator; import com.google.common.primitives.UnsignedBytes; import qora.transaction.Transaction; import utils.ObserverMessage; import utils.ReverseComparator; import database.DBSet; import database.serializer.TransactionSerializer; public class TransactionMap extends DBMap<byte[], Transaction> implements Observer { public static final int TIMESTAMP_INDEX = 1; private Map<Integer, Integer> observableData = new HashMap<Integer, Integer>(); public TransactionMap(DBSet databaseSet, DB database) { super(databaseSet, database); this.observableData.put(DBMap.NOTIFY_ADD, ObserverMessage.ADD_TRANSACTION_TYPE); this.observableData.put(DBMap.NOTIFY_REMOVE, ObserverMessage.REMOVE_TRANSACTION_TYPE); this.observableData.put(DBMap.NOTIFY_LIST, ObserverMessage.LIST_TRANSACTION_TYPE); } public TransactionMap(TransactionMap parent) { super(parent); } @SuppressWarnings({ "unchecked", "rawtypes" }) protected void createIndexes(DB database) { //TIMESTAMP INDEX Tuple2Comparator<Long, byte[]> comparator = new Fun.Tuple2Comparator<Long, byte[]>(Fun.COMPARATOR, UnsignedBytes.lexicographicalComparator()); NavigableSet<Tuple2<Integer, byte[]>> heightIndex = database.createTreeSet("transactions_index_timestamp") .comparator(comparator) .makeOrGet(); NavigableSet<Tuple2<Integer, byte[]>> descendingHeightIndex = database.createTreeSet("transactions_index_timestamp_descending") .comparator(new ReverseComparator(comparator)) .makeOrGet(); createIndex(TIMESTAMP_INDEX, heightIndex, descendingHeightIndex, new Fun.Function2<Long, byte[], Transaction>() { @Override public Long run(byte[] key, Transaction value) { return value.getTimestamp(); } }); } @Override protected Map<byte[], Transaction> getMap(DB database) { //OPEN MAP return database.createTreeMap("transactions") .keySerializer(BTreeKeySerializer.BASIC) .comparator(UnsignedBytes.lexicographicalComparator()) .valueSerializer(new TransactionSerializer()) .counterEnable() .makeOrGet(); } @Override protected Map<byte[], Transaction> getMemoryMap() { return new TreeMap<byte[], Transaction>(UnsignedBytes.lexicographicalComparator()); } @Override protected Transaction getDefaultValue() { return null; } @Override protected Map<Integer, Integer> getObservableData() { return this.observableData; } @Override public void update(Observable o, Object arg) { ObserverMessage message = (ObserverMessage) arg; //ON NEW BLOCK if(message.getType() == ObserverMessage.ADD_BLOCK_TYPE) { //CLEAN UP for(Transaction transaction: this.getValues()) { //CHECK IF DEADLINE PASSED if(transaction.getDeadline() < NTP.getTime()) { this.delete(transaction.getSignature()); //NOTIFY /*this.setChanged(); this.notifyObservers(new ObserverMessage(ObserverMessage.REMOVE_TRANSACTION_TYPE, transaction));*/ } } } } public void add(Transaction transaction) { this.set(transaction.getSignature(), transaction); } public List<Transaction> getTransactions() { return new ArrayList<Transaction>(this.getValues()); } public void delete(Transaction transaction) { this.delete(transaction.getSignature()); } public boolean contains(Transaction transaction) { return this.contains(transaction.getSignature()); } }