All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.descoped.rawdata.memory.MemoryRawdataTopic Maven / Gradle / Ivy

The newest version!
package io.descoped.rawdata.memory;

import de.huxhorn.sulky.ulid.ULID;
import io.descoped.rawdata.api.RawdataMessage;

import java.util.Map;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import static java.util.Optional.ofNullable;

class MemoryRawdataTopic {

    final String topic;
    final NavigableMap data = new ConcurrentSkipListMap<>(); // protected by lock
    final ReentrantLock lock = new ReentrantLock();
    final Condition condition = lock.newCondition();

    MemoryRawdataTopic(String topic) {
        this.topic = topic;
    }

    RawdataMessage lastMessage() {
        checkHasLock();
        if (data.isEmpty()) {
            return null;
        }
        return data.lastEntry().getValue();
    }

    private void checkHasLock() {
        if (!lock.isHeldByCurrentThread()) {
            throw new IllegalStateException("The calling thread must hold the lock before calling this method");
        }
    }

    void write(ULID.Value ulid, RawdataMessage message) {
        checkHasLock();
        RawdataMessage copy = copy(ulid, message); // fake serialization and deserialization
        data.put(copy.ulid(), copy);
        signalProduction();
    }

    private RawdataMessage copy(ULID.Value ulid, RawdataMessage original) {
        return original.copy()
                .ulid(ulid)
                .data(original.keys().stream().collect(Collectors.toMap(
                                k -> k, k -> {
                                    byte[] src = original.get(k);
                                    byte[] dest = new byte[src.length];
                                    System.arraycopy(src, 0, dest, 0, src.length);
                                    return src;
                                }
                        ))
                )
                .build();
    }

    boolean hasNext(MemoryCursor cursor) {
        checkHasLock();
        if (cursor.startKey == null) {
            return !data.isEmpty();
        }
        if (cursor.inclusive) {
            return data.ceilingEntry(cursor.startKey) != null;
        } else {
            return data.higherKey(cursor.startKey) != null;
        }
    }

    RawdataMessage readNext(MemoryCursor cursor) {
        checkHasLock();
        if (cursor.startKey == null) {
            return data.firstEntry().getValue();
        }
        if (cursor.inclusive) {
            return ofNullable(data.ceilingEntry(cursor.startKey)).map(Map.Entry::getValue).orElse(null);
        } else {
            return ofNullable(data.higherEntry(cursor.startKey)).map(Map.Entry::getValue).orElse(null);
        }
    }

    boolean tryLock() {
        return lock.tryLock();
    }

    void tryLock(int timeout, TimeUnit unit) {
        try {
            if (!lock.tryLock(timeout, unit)) {
                throw new RuntimeException("timeout while waiting for lock");
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    void unlock() {
        lock.unlock();
    }

    void awaitProduction(long duration, TimeUnit unit) throws InterruptedException {
        condition.await(duration, unit);
    }

    void signalProduction() {
        condition.signalAll();
    }

    @Override
    public String toString() {
        return "MemoryRawdataTopic{" +
                "topic='" + topic + '\'' +
                '}';
    }

    ULID.Value ulidOf(String position, ULID.Value lowerBound, ULID.Value upperBound) {
        checkHasLock();

        /*
         * Perform a topic scan looking for the given position, starting at lowerBound (inclusive) and ending at upperBound (exclusive)
         */

        for (Map.Entry entry : data.subMap(lowerBound, true, upperBound, false).entrySet()) {
            if (position.equals(entry.getValue().position())) {
                return entry.getKey();
            }
        }

        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy