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

com.kolibrifx.plovercrest.server.internal.local.LocalTable Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010-2017, KolibriFX AS. Licensed under the Apache License, version 2.0.
 */

package com.kolibrifx.plovercrest.server.internal.local;

import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.kolibrifx.common.Disposable;
import com.kolibrifx.plovercrest.client.RangeQuery;
import com.kolibrifx.plovercrest.client.Table;
import com.kolibrifx.plovercrest.client.TableMetadata;
import com.kolibrifx.plovercrest.client.TableReader;
import com.kolibrifx.plovercrest.client.TableSerializer;
import com.kolibrifx.plovercrest.client.TableSubscriber;
import com.kolibrifx.plovercrest.client.TableWriter;
import com.kolibrifx.plovercrest.client.internal.ErrorListenerSubject;
import com.kolibrifx.plovercrest.client.internal.SafeTableSubscriber;
import com.kolibrifx.plovercrest.client.internal.SubscriptionQuery;
import com.kolibrifx.plovercrest.client.internal.SubscriptionQuery.QueryKind;
import com.kolibrifx.plovercrest.server.internal.StreamObservers;
import com.kolibrifx.plovercrest.server.streams.Stream;
import com.kolibrifx.plovercrest.server.streams.StreamObserver;

public class LocalTable extends Table {
    private class LocalTableSubscriptionObserver implements StreamObserver {
        private long lastTimestamp;
        private long lastValidTimestamp = -1;
        private final SafeTableSubscriber subscriber;
        private volatile boolean closed;

        LocalTableSubscriptionObserver(final SafeTableSubscriber subscriber) {
            this.subscriber = subscriber;
            closed = false;
        }

        @Override
        public void onObserve(final long timestamp, final long index, final ByteBuffer bytes) {
            lastTimestamp = timestamp;
            final T element = serializer.unserialize(timestamp, bytes);
            if (!closed) {
                subscriber.receive(getName(), element);
            }
        }

        @Override
        public void onObserveEnd(final long lastValidTimestamp, final long elementCount) {
            if (!closed && lastValidTimestamp > lastTimestamp && lastValidTimestamp > this.lastValidTimestamp) {
                subscriber.receiveLastValidTimestamp(getName(), lastValidTimestamp);
            }
            this.lastValidTimestamp = lastValidTimestamp;
        }

        @Override
        public void onObserveFrozen() {
            if (!closed) {
                subscriber.receiveTableFrozen(getName());
                // Close locally, but for now, do not remove from subscribers.
                // Rationale: when a table delete event is added, we have to handle it in this class.
                close();
            }
        }

        // Call this to prevent subscriber from being triggered in the future,
        // resolving thread race conditions in removeSubscriber()
        void close() {
            closed = true;
        }
    }

    private final Stream stream;
    private final StreamObservers streamObservers;
    private final Map, LocalTableSubscriptionObserver> subscriberObserverMap;
    private final ErrorListenerSubject errorListenerSubject;

    public LocalTable(final Stream stream,
                      final String name,
                      final Class elementClass,
                      final TableSerializer serializer,
                      final TableMetadata metadata,
                      final StreamObservers streamObservers,
                      final ErrorListenerSubject errorListenerSubject) {
        super(name, elementClass, serializer, metadata);
        this.stream = stream;
        this.streamObservers = streamObservers;
        this.errorListenerSubject = errorListenerSubject;
        this.subscriberObserverMap =
                Collections.synchronizedMap(new HashMap, LocalTableSubscriptionObserver>());
    }

    @Override
    public TableReader getReader(final RangeQuery range) {
        return new LocalTableReader(stream, serializer, elementClass, range, name);
    }

    @Override
    public TableWriter getWriter() {
        return new LocalTableWriter(stream.getWriter(), serializer, errorListenerSubject);
    }

    @Override
    public long getFirstTimestamp() {
        return stream.getFirstTimestamp();
    }

    @Override
    public long getLastTimestamp() {
        return stream.getLastTimestamp();
    }

    @Override
    public long getDataLength() {
        return stream.getDataLengthInBytes();
    }

    @Override
    public long getLastValidTimestamp() {
        return stream.getLastValidTimestamp();
    }

    private Disposable subscribe(final SubscriptionQuery query, final TableSubscriber subscriber) {
        if (subscriberObserverMap.containsKey(subscriber)) {
            throw new IllegalArgumentException("The same subscriber was added more than once");
        }
        final LocalTableSubscriptionObserver observer =
                new LocalTableSubscriptionObserver(new SafeTableSubscriber<>(subscriber));
        subscriberObserverMap.put(subscriber, observer);
        final Disposable subscription = streamObservers.subscribe(getName(), query, observer, ByteBuffer.class);
        return new Disposable() {
            @Override
            public void close() {
                final LocalTableSubscriptionObserver observer = subscriberObserverMap.get(subscriber);
                if (observer == null) {
                    return;
                }
                observer.close();
                subscriberObserverMap.remove(subscriber);
                subscription.close();
            }
        };
    }

    @Override
    public Disposable addSubscriber(final long fromTimestamp, final TableSubscriber subscriber) {
        return subscribe(new SubscriptionQuery(QueryKind.TIMESTAMP, fromTimestamp), subscriber);
    }

    @Override
    public Disposable addSubscriberFromIndex(final long fromIndex, final TableSubscriber subscriber) {
        return subscribe(new SubscriptionQuery(QueryKind.INDEX, fromIndex), subscriber);
    }

    @Override
    public long getElementCount() {
        return stream.getEntryCount();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy