co.runrightfast.incubator.rx.impl.ObservableRingBufferImpl.java Source code

Java tutorial

Introduction

Here is the source code for co.runrightfast.incubator.rx.impl.ObservableRingBufferImpl.java

Source

/*
 Copyright 2015 Alfio Zappala
    
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
    
 http://www.apache.org/licenses/LICENSE-2.0
    
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
package co.runrightfast.incubator.rx.impl;

import co.runrightfast.commons.utils.JsonUtils;
import co.runrightfast.incubator.commons.disruptor.DisruptorConfig;
import co.runrightfast.incubator.commons.disruptor.RingBufferReference;
import co.runrightfast.incubator.rx.ObservableRingBuffer;
import co.runrightfast.logging.JsonLog;
import com.google.common.util.concurrent.AbstractIdleService;
import com.google.gson.JsonObject;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.TimeoutException;
import com.lmax.disruptor.dsl.Disruptor;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.extern.java.Log;
import org.apache.commons.collections4.CollectionUtils;
import rx.Observable;
import rx.Subscriber;

/**
 *
 * @author alfio
 * @param <A> ring buffer item type
 */
@RequiredArgsConstructor
@Log
public class ObservableRingBufferImpl<A> extends AbstractIdleService implements ObservableRingBuffer<A> {

    private static final String CLAZZ = ObservableRingBufferImpl.class.getName();

    @NonNull
    private final DisruptorConfig<A> disruptorConfig;
    @NonNull
    private final Class<A> ringBufferDataType;

    private Disruptor<RingBufferReference<A>> disruptor;
    private RingBuffer<RingBufferReference<A>> ringBuffer;

    @Getter
    private Observable<A> observable;

    private final List<Subscriber<? super A>> subscribers = new CopyOnWriteArrayList<>();

    private static final JsonLog warning = JsonLog.warningJsonLog(log, CLAZZ);

    @Value
    @Builder
    private static class LogMessage {

        String message;

        String type;

        long sequence;

    }

    @Override
    protected void startUp() {
        this.disruptor = disruptorConfig.newDisruptor(ringBufferDataType);
        this.observable = Observable.<A>create(this::observableOnSubscribe);
        this.disruptor.handleEventsWith(this::onEvent);
        this.ringBuffer = this.disruptor.start();
    }

    @Override
    protected void shutDown() {
        if (this.disruptor != null) {
            for (int i = 1; true; i++) {
                final int waitTimeSecs = 10;
                try {
                    this.disruptor.shutdown(waitTimeSecs, TimeUnit.SECONDS);
                    break;
                } catch (final TimeoutException ex) {
                    log.logp(Level.WARNING, CLAZZ, "shutDown", "waiting for disruptor to shutdown : {0} secs",
                            i * waitTimeSecs);
                }
            }
        }

        if (this.subscribers != null) {
            this.subscribers.stream().filter(this::isSubscribed).forEach(subscriber -> subscriber.onCompleted());
        }

        this.disruptor = null;
        this.ringBuffer = null;
        this.subscribers.clear();
    }

    @Override
    public boolean publish(@NonNull final A msg) {
        return this.ringBuffer.tryPublishEvent(this::setRingBufferReferenceData, msg);
    }

    private void setRingBufferReferenceData(final RingBufferReference<A> msg, final long sequence, final A data) {
        msg.data = data;
    }

    private boolean isSubscribed(final Subscriber<? super A> subscriber) {
        return !subscriber.isUnsubscribed();
    }

    private void observableOnSubscribe(final Subscriber<? super A> subscriber) {
        if (!subscriber.isUnsubscribed()) {
            this.subscribers.add(subscriber);
        }
    }

    // TODO: collect metrics - how many event were dropped, delivered, etc
    private void onEvent(final RingBufferReference<A> msg, final long sequence, final boolean endOfBatch) {
        if (CollectionUtils.isEmpty(subscribers)) {
            warning.log("onEvent", new LogMessage("message dropped because there are no observers",
                    getClass().getName(), sequence));
            return;
        }

        for (final Subscriber<? super A> subscriber : subscribers) {
            if (subscriber.isUnsubscribed()) {
                this.subscribers.remove(subscriber);
                continue;
            }
            try {
                subscriber.onNext(msg.data);
            } catch (final Throwable t) {
                subscriber.onError(t);
                this.subscribers.remove(subscriber);
            }
        }

    }

    @Override
    public long remainingCapacity() {
        return ringBuffer.getBufferSize();
    }

    @Override
    public int observerCount() {
        return subscribers.size();
    }

    @Override
    public int bufferSize() {
        return ringBuffer.getBufferSize();
    }

    @Override
    public long cursor() {
        return ringBuffer.getCursor();
    }

    @Override
    public String toString() {
        if (!isRunning()) {
            return "{}";
        }
        final JsonObject json = new JsonObject();
        json.addProperty("remainingCapacity", remainingCapacity());
        json.addProperty("observerCount", observerCount());
        json.addProperty("bufferSize", bufferSize());
        json.addProperty("cursor", cursor());
        json.addProperty("ringBufferDataType", ringBufferDataType.getName());
        return JsonUtils.prettyPrintingGson.toJson(json);
    }

}