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

com.canoo.dolphin.server.event.impl.DolphinEventBusImpl Maven / Gradle / Ivy

Go to download

The Dolphin Platform is a framework that implements the presentation model pattern and provides amodern way to create enterprise applications. The Platform provides several client implementations that all canbe used in combination with a general sever API.

There is a newer version: 1.0.0.CR5
Show newest version
/*
 * Copyright 2015 Canoo Engineering AG.
 *
 * 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 com.canoo.dolphin.server.event.impl;

import com.canoo.dolphin.event.Subscription;
import com.canoo.dolphin.server.context.DolphinContext;
import com.canoo.dolphin.server.event.DolphinEventBus;
import com.canoo.dolphin.server.event.Message;
import com.canoo.dolphin.server.event.MessageListener;
import groovyx.gpars.dataflow.DataflowQueue;
import org.opendolphin.StringUtil;
import org.opendolphin.core.server.EventBus;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

public class DolphinEventBusImpl implements DolphinEventBus {

    private static final DolphinEventBusImpl instance = new DolphinEventBusImpl();
    private static final int MAX_POLL_DURATION = 100;
    public static final int TIMEOUT = 20;

    public static DolphinEventBusImpl getInstance() {
        return instance;
    }

    private final EventBus eventBus = new EventBus();

    private final DataflowQueue sender = new DataflowQueue();

    private final Object releaseVal = new Object();

    //access is only concurrent for different keys. This sync strategy should be sufficient
    private Map receiverPerSession = new ConcurrentHashMap<>();

    protected DolphinEventBusImpl() {
    }

    public void publish(final String topic, final Object data) {
        if(StringUtil.isBlank(topic)) {
            throw new IllegalArgumentException("topic mustn't be empty!");
        }
        eventBus.publish(sender, new MessageImpl(topic, data));
    }

    public void triggerTaskExecution() {
        eventBus.publish(sender, new TaskTrigger(){});
    }

    public Subscription subscribe(final String topic, final MessageListener handler) {
        if(StringUtil.isBlank(topic)) {
            throw new IllegalArgumentException("topic mustn't be empty!");
        }
        if(handler == null) {
            throw new IllegalArgumentException("handler mustn't be empty!");
        }
        String dolphinId = getDolphinId();
        if (dolphinId == null) {
            throw new IllegalStateException("subscribe was called outside a dolphin session");
        }
        return getOrCreateReceiverInSession(dolphinId).subscribe(topic, handler);
    }

    protected String getDolphinId() {
        return DolphinContext.getCurrentContext().getId();
    }

    public void unsubscribeSession(final String dolphinId) {
        if(StringUtil.isBlank(dolphinId)) {
            throw new IllegalArgumentException("dolphinId mustn't be empty!");
        }
        Receiver receiver = receiverPerSession.remove(dolphinId);
        if (receiver != null) {
            receiver.unregister(eventBus);
        }
    }

    private Receiver getOrCreateReceiverInSession(String dolphinId) {
        if(StringUtil.isBlank(dolphinId)) {
            throw new IllegalArgumentException("dolphinId mustn't be empty!");
        }
        Receiver receiver = receiverPerSession.get(dolphinId);
        if (receiver == null) {
            receiver = new Receiver();
            receiverPerSession.put(dolphinId, receiver);
        }
        return receiver;
    }


    /**
     * this method blocks till a release event occurs or there is something to handle in this session.
     * This is the only location where we subscribe to our internal eventBus.
     * So if longPoll is not called from client, we will never listen to the eventBus.
     */
    public void longPoll() throws InterruptedException {
        final String dolphinId = getDolphinId();
        if (dolphinId == null) {
            throw new IllegalStateException("longPoll was called outside a dolphin session");
        }
        //TODO replace by log
//        System.out.println("long poll call from dolphin session " + dolphinId);
        final Receiver receiverInSession = getOrCreateReceiverInSession(dolphinId);
        if (!receiverInSession.isListeningToEventBus()) {
            receiverInSession.register(eventBus);
        }
        final DataflowQueue receiverQueue = receiverInSession.getReceiverQueue();

        boolean somethingHandled = false;

        while (!somethingHandled) {
            //blocking call
            Object val = receiverQueue.getVal();

            final long startTime = System.currentTimeMillis();

            while (val != null) {
                if (val == releaseVal) {
                    return;
                }
                if(val instanceof  Message) {
                    final Message event = (Message) val;
                    //TODO replace by log
//                    System.out.println("handle event for dolphinId: " + dolphinId);
                    somethingHandled |= receiverInSession.handle(event);
                } else if(TaskTrigger.class.isAssignableFrom(val.getClass())) {
                    somethingHandled |= DolphinContext.getCurrentContext().getTaskExecutor().execute();
                }

                //if there are many events we would loop forever -> additional exit condition
                if (System.currentTimeMillis() - startTime <= MAX_POLL_DURATION) {
                    val = receiverQueue.getVal(TIMEOUT, MILLISECONDS);
                } else {
                    val = null;
                }
            }
        }
    }

    /**
     * TODO we must release per session!!!
     */
    @SuppressWarnings("unchecked")
    public void release() {
//        //TODO the release should happen in the context of a dolphin session.
//        //TODO this piece of code should be used then
//        String dolphinId = getId();
//        if (dolphinId == null) {
//            //TODO replace by log
//            System.out.println("warning: release was called outside dolphin session");
//            //TODO warn or throw exception?
//            return;
//        }
//        Receiver receiver = receiverPerSession.get(dolphinId);
//        if (receiver != null && receiver.isListeningToEventBus()) {
//            receiver.getReceiverQueue().leftShift(releaseVal);
//        }


        //TODO remove
        for (Receiver receiver : receiverPerSession.values()) {
            if (receiver.isListeningToEventBus()) {
                receiver.getReceiverQueue().leftShift(releaseVal);
            }
        }
    }

    private final static class MessageImpl implements Message {

        private final String topic;

        private final Object data;

        private final long timestamp;

        private MessageImpl(String topic, Object data) {
            this.topic = topic;
            this.data = data;
            this.timestamp = System.currentTimeMillis();
        }

        @Override
        public String getTopic() {
            return topic;
        }

        @Override
        public Object getData() {
            return data;
        }

        @Override
        public long getSendTimestamp() {
            return timestamp;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy