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

com.kolibrifx.plovercrest.server.local.DefaultPlovercrestLocal 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.local;

import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
import com.kolibrifx.common.Disposable;
import com.kolibrifx.plovercrest.client.AbstractPlovercrest;
import com.kolibrifx.plovercrest.client.DefaultTableListInfo;
import com.kolibrifx.plovercrest.client.MultiTableWriter;
import com.kolibrifx.plovercrest.client.MultiTableWriterSerializerFactory;
import com.kolibrifx.plovercrest.client.PlovercrestException;
import com.kolibrifx.plovercrest.client.PlovercrestLocal;
import com.kolibrifx.plovercrest.client.Table;
import com.kolibrifx.plovercrest.client.TableListInfo;
import com.kolibrifx.plovercrest.client.TableMetadata;
import com.kolibrifx.plovercrest.client.TableNamesObserver;
import com.kolibrifx.plovercrest.client.TableSerializer;
import com.kolibrifx.plovercrest.client.TableSerializerFactory;
import com.kolibrifx.plovercrest.client.TableSubscriber;
import com.kolibrifx.plovercrest.client.internal.SafeTableNamesObserver;
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.client.internal.shared.TableMetadataUtils;
import com.kolibrifx.plovercrest.server.PlovercrestEngine;
import com.kolibrifx.plovercrest.server.PlovercrestEngineBuilder;
import com.kolibrifx.plovercrest.server.TableInfo;
import com.kolibrifx.plovercrest.server.internal.EventDispatcher;
import com.kolibrifx.plovercrest.server.internal.StreamObservers;
import com.kolibrifx.plovercrest.server.internal.ThreadedEventDispatcher;
import com.kolibrifx.plovercrest.server.internal.local.LocalMultiTableWriter;
import com.kolibrifx.plovercrest.server.internal.local.LocalTable;
import com.kolibrifx.plovercrest.server.streams.PlovercrestStreamEngineBuilder;
import com.kolibrifx.plovercrest.server.streams.Stream;
import com.kolibrifx.plovercrest.server.streams.StreamEngine;
import com.kolibrifx.plovercrest.server.streams.StreamNamesListener;
import com.kolibrifx.plovercrest.server.streams.StreamObserver;

public class DefaultPlovercrestLocal extends AbstractPlovercrest implements PlovercrestLocal {
    private final PlovercrestEngine engine;
    private final StreamObservers streamObservers;
    private final String dataPath;
    private final TableSerializerFactory serializerFactory;
    private final StreamEngine streamEngine;

    public DefaultPlovercrestLocal(final TableSerializerFactory serializerFactory, final String dataPath) {
        this(serializerFactory, dataPath, new ThreadedEventDispatcher());
    }

    public DefaultPlovercrestLocal(final TableSerializerFactory serializerFactory,
                                   final String dataPath,
                                   final EventDispatcher dispatcher) {
        this.serializerFactory = serializerFactory;
        this.dataPath = dataPath;
        this.engine = new PlovercrestEngineBuilder(dataPath).dispatcher(dispatcher).build();
        this.streamEngine = new PlovercrestStreamEngineBuilder(engine).serializerFactory(serializerFactory).build();
        this.streamObservers = new StreamObservers(streamEngine);
    }

    // TODO: (#34) This should support streams
    @Override
    public Collection list() {
        return engine.getTableNames().stream().map(name -> new DefaultTableListInfo(name, true))
                     .collect(Collectors.toList());
    }

    private  LocalTable createImpl(final String name, final Class elementClass,
                                         final TableSerializer serializer) {
        final TableInfo info =
                new TableInfo(name, TableMetadataUtils.createFromSerializer(serializer, serializerFactory));
        streamEngine.create(info);
        // We open as ByteBuffer because there is no way to pass the serializer
        final Stream stream = streamEngine.openRaw(name);
        return new LocalTable(stream, name, elementClass, serializer, info.getMetadata(), streamObservers,
                                 errorListenerSubject);
    }

    @Override
    public  Table open(final String name, final Class clazz) {
        try {
            final Stream stream = streamEngine.openRaw(name);
            if (stream == null) {
                return null;
            }
            return openLocalTable(clazz, stream, name);
        } catch (final PlovercrestException e) {
            errorListenerSubject.onOpenError(e);
            throw e;
        }
    }

    @Override
    public TableMetadata getMetadata(final String name) {
        final com.kolibrifx.plovercrest.server.Table table = engine.open(name);
        if (table == null) {
            return null;
        } else {
            return table.getInfo().getMetadata();
        }
    }

    private  LocalTable openLocalTable(final Class clazz, final Stream stream, final String name) {
        final TableSerializer serializer;
        try {
            serializer = serializerFactory.createSerializer(stream.getMetadata(), clazz);
        } catch (final PlovercrestException e) {
            // include table name in error message
            throw new PlovercrestException("Failed to create serializer for table '" + name + "'. " + e.getMessage(),
                                           e);
        }
        return new LocalTable(stream, name, clazz, serializer, stream.getMetadata(), streamObservers,
                                 errorListenerSubject);
    }

    @Override
    public void close() {
        streamObservers.close();
        streamEngine.close();
        engine.close();
    }

    @Override
    public boolean delete(final String name) {
        return engine.delete(name);
    }

    @Override
    public boolean rename(final String oldName, final String newName) {
        return engine.rename(oldName, newName) != null;
    }

    @Override
    public  MultiTableWriter createMultiTableWriter(final MultiTableWriterSerializerFactory factory,
                                                          final MultiTableWriter.WriterMode mode) {
        return new LocalMultiTableWriter(this, streamEngine, factory, mode);
    }

    @Override
    public String getDataPath() {
        return dataPath;
    }

    @Override
    public  Disposable addSubscriber(final String tableName, final Class elementClass, final long fromTimestamp,
                                        final TableSubscriber subscriber) {
        open(tableName, elementClass); // to throw exception if e.g. type definition is wrong
        final SafeTableSubscriber safeSubscriber = new SafeTableSubscriber<>(subscriber);
        final StreamObserver observer = new StreamObserver() {
            private TableSerializer serializer;
            private long lastTimestamp = -1;

            T deserialize(final long timestamp, final ByteBuffer bytes) {
                if (serializer == null) {
                    // It should normally be possible to open the table and get the serializer from it at this point.
                    // If not, use the TableSerializerFactory as a fallback.
                    final Table table = open(tableName, elementClass);
                    if (table == null) {
                        serializer = serializerFactory.createSerializer(elementClass);
                    } else {
                        serializer = table.getSerializer();
                    }
                }
                return serializer.unserialize(timestamp, bytes);
            }

            @Override
            public void onObserve(final long timestamp, final long index, final ByteBuffer bytes) {
                safeSubscriber.receive(tableName, deserialize(timestamp, bytes));
                lastTimestamp = timestamp;
            }

            @Override
            public void onObserveEnd(final long lastValidTimestamp, final long elementCount) {
                if (lastValidTimestamp > lastTimestamp) {
                    safeSubscriber.receiveLastValidTimestamp(tableName, lastValidTimestamp);
                }
            }

            @Override
            public void onObserveFrozen() {
                safeSubscriber.receiveTableFrozen(tableName);
            }
        };
        return streamObservers.subscribe(tableName, new SubscriptionQuery(QueryKind.TIMESTAMP, fromTimestamp), observer,
                                         ByteBuffer.class);
    }

    PlovercrestEngine getEngine() {
        return engine;
    }

    @Override
    public Disposable addTableNamesObserver(final TableNamesObserver listener) {
        final SafeTableNamesObserver observer = new SafeTableNamesObserver(listener);
        return streamEngine.addStreamNamesListener(new StreamNamesListener() {
            @Override
            public void onRemoved(final Set names) {
                for (final String name : names) {
                    observer.onTableDeleted(name);
                }
            }

            @Override
            public void onInit(final Set allNames) {
                observer.onTablesReset(allNames);
            }

            @Override
            public void onAdded(final Set names) {
                for (final String name : names) {
                    observer.onTableCreated(name);
                }
            }
        });
    }

    @Override
    public  Table create(final String name, final Class elementClass) {
        final TableSerializer serializer = serializerFactory.createSerializer(elementClass);
        return createImpl(name, elementClass, serializer);
    }

    @Override
    public  Table create(final String name, final TableSerializer serializer) {
        return createImpl(name, serializer.elementClass(), serializer);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy