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

com.kolibrifx.plovercrest.server.streams.PluggableStreamEngine 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.streams;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.apache.log4j.Logger;
import com.kolibrifx.common.Disposable;
import com.kolibrifx.plovercrest.client.PlovercrestException;
import com.kolibrifx.plovercrest.server.TableInfo;
import com.kolibrifx.plovercrest.server.internal.streams.MultiProviderStreamNamesListener;

/**
 * Basic pluggable {@link StreamEngine} implementation. Most production code should use
 * {@link PlovercrestStreamEngineBuilder} instead constructing this class directly; it is exposed
 * for unit testing purposes.
 */
public class PluggableStreamEngine implements StreamEngine {
    private static final Logger log = Logger.getLogger(PluggableStreamEngine.class);

    private final Collection providers = new ArrayList<>();
    private final Collection disposables = new ArrayList<>();

    @Override
    public  Stream open(final String name, final Class elementType) {
        for (final StreamProvider provider : providers) {
            try {
                final Stream stream = provider.tryOpen(this, name, elementType);
                if (stream != null) {
                    return stream;
                }
            } catch (final PlovercrestException e) {
                log.error(e.getMessage(), e);
                throw e;
            } catch (final RuntimeException e) {
                log.error(e.getMessage(), e);
                throw new PlovercrestException("Error opening '" + name + "': " + e.getMessage(), e);
            }
        }
        return null;
    }

    @Override
    public Stream openRaw(final String name) {
        return open(name, ByteBuffer.class);
    }

    public void addProvider(final StreamProvider provider) {
        providers.add(provider);
    }

    @Override
    public Collection list() {
        return providers.stream().flatMap(x -> x.list().stream()).collect(Collectors.toList());
    }

    @Override
    public void create(final TableInfo info) {
        final Stream stream = openRaw(info.getName());
        if (stream != null) {
            throw new PlovercrestException("A stream named " + info.getName() + " already exists");
        }
        for (final StreamProvider p : providers) {
            final boolean created = p.tryCreate(info);
            if (created) {
                return;
            }
        }
        throw new PlovercrestException("No provider was able to create a stream named " + info.getName());
    }

    @Override
    public boolean delete(final String name) {
        final Stream stream = openRaw(name);
        if (stream == null) {
            return false;
        } else {
            return stream.delete();
        }
    }

    @Override
    public boolean rename(final String oldName, final String newName) {
        if (oldName.equals(newName)) {
            return false;
        }
        if (openRaw(newName) != null) {
            return false;
        }
        final Stream oldTable = open(oldName, ByteBuffer.class);
        if (oldTable == null) {
            return false;
        }
        return oldTable.rename(newName);
    }

    @Override
    public void close() {
        for (final Disposable d : disposables) {
            d.close();
        }
        disposables.clear();
    }

    @Override
    public Disposable addStreamNamesListener(final StreamNamesListener listener) {
        return new MultiProviderStreamNamesListener(providers, listener);
    }

    @Override
    public Disposable addCreateListener(final String name, final StreamCreateListener listener) {
        final AtomicBoolean done = new AtomicBoolean(false);
        final AtomicReference disposableRef = new AtomicReference<>();
        disposableRef.set(addStreamNamesListener(new StreamNamesListener() {
            @Override
            public void onRemoved(final Set names) {
                // ignored
            }

            @Override
            public void onInit(final Set allNames) {
                processNames(allNames);
            }

            @Override
            public void onAdded(final Set names) {
                processNames(names);
            }

            private void processNames(final Set names) {
                if (names.contains(name) && done.compareAndSet(false, true)) {
                    listener.onCreated(name);
                    // disposableRef can be unset due to reentrancy
                    if (disposableRef.get() != null) {
                        disposableRef.get().close();
                    }
                }
            }
        }));
        if (done.get()) {
            // can happen due to reentrancy
            disposableRef.get().close();
        }
        return new Disposable() {
            @Override
            public void close() {
                done.set(true);
                disposableRef.get().close();
            }
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy