com.kolibrifx.plovercrest.server.internal.local.LocalTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plovercrest-server Show documentation
Show all versions of plovercrest-server Show documentation
Plovercrest server library.
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();
}
}