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

com.canoo.dp.impl.server.event.AbstractEventBus 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.

The newest version!
/*
 * Copyright 2015-2018 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.dp.impl.server.event;

import com.canoo.dp.impl.platform.core.Assert;
import com.canoo.dp.impl.server.client.ClientSessionLifecycleHandler;
import com.canoo.dp.impl.server.context.DolphinContext;
import com.canoo.dp.impl.server.context.DolphinContextProvider;
import com.canoo.platform.core.functional.Subscription;
import com.canoo.platform.remoting.server.event.ClientSessionEventFilter;
import com.canoo.platform.remoting.server.event.MessageEventContext;
import com.canoo.platform.remoting.server.event.MessageListener;
import com.canoo.platform.remoting.server.event.RemotingEventBus;
import com.canoo.platform.remoting.server.event.Topic;
import com.canoo.platform.server.client.ClientSession;
import org.apiguardian.api.API;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpSession;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;

import static org.apiguardian.api.API.Status.INTERNAL;

@API(since = "0.x", status = INTERNAL)
public abstract class AbstractEventBus implements RemotingEventBus {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractEventBus.class);

    private DolphinContextProvider contextProvider;

    private final Map, List>> topicToListenerMap = new ConcurrentHashMap<>();

    private final Map, String> listenerToSessionMap = new ConcurrentHashMap<>();

    private final Map> sessionStore = new ConcurrentHashMap<>();

    private final AtomicBoolean initialized = new AtomicBoolean(false);

    public void init(final DolphinContextProvider contextProvider, final ClientSessionLifecycleHandler lifecycleHandler) {
        this.contextProvider = Assert.requireNonNull(contextProvider, "contextProvider");
        Assert.requireNonNull(lifecycleHandler, "lifecycleHandler").addSessionDestroyedListener((s) -> onSessionEnds(s.getId()));
        initialized.set(true);
    }

    @Override
    public  void publish(final Topic topic, final T data) {
        checkInitialization();

        final DolphinEvent event = new DolphinEvent(topic, System.currentTimeMillis(), data);

        event.addMetadata(EventConstants.TYPE_PARAM, EventConstants.TYPE_PLATFORM);

        final DolphinContext currentContext = getCurrentContext();
        if (currentContext != null) {
            final ClientSession clientSession = currentContext.getClientSession();
            if (clientSession != null) {
                event.addMetadata(EventConstants.CLIENT_SESSION_PARAM, clientSession.getId());
                final HttpSession httpSession = clientSession.getHttpSession();
                if (httpSession != null) {
                    event.addMetadata(EventConstants.HTTP_SESSION_PARAM, httpSession.getId());
                }
            }
        }
        //Handle listener in same session
        if (currentContext != null) {
            final List> listenersInCurrentSession = getListenersForSessionAndTopic(currentContext.getId(), topic);
            for (ListenerWithFilter listenerAndFilter : listenersInCurrentSession) {
                final Predicate> filter = listenerAndFilter.getFilter();
                final MessageListener listener = listenerAndFilter.getListener();
                if (filter == null || filter.test(event.getMessageEventContext())) {
                    listener.onMessage(event);
                }
            }
        }
        publishForOtherSessions(event);
    }

    @Override
    public  Subscription subscribe(final Topic topic, final MessageListener listener, final Predicate> filter) {
        checkInitialization();
        Assert.requireNonNull(topic, "topic");
        Assert.requireNonNull(listener, "listener");

        final DolphinContext subscriptionContext = getCurrentContext();
        if (subscriptionContext == null) {
            throw new IllegalStateException("Subscription can only be done from Dolphin Context!");
        }
        final String subscriptionSessionId = subscriptionContext.getId();
        LOG.trace("Adding subscription for topic {} in Dolphin Platform context {}", topic.getName(), subscriptionSessionId);
        List> listeners = topicToListenerMap.get(topic);
        if (listeners == null) {
            listeners = new CopyOnWriteArrayList<>();
            topicToListenerMap.put(topic, listeners);
        }
        final ListenerWithFilter listenerWithFilter = new ListenerWithFilter(listener, filter);
        listeners.add(listenerWithFilter);
        listenerToSessionMap.put(listener, subscriptionSessionId);
        final Subscription subscription = new Subscription() {
            @Override
            public void unsubscribe() {
                LOG.trace("Removing subscription for topic {} in Dolphin Platform context {}", topic.getName(), subscriptionSessionId);
                final List> listeners = topicToListenerMap.get(topic);
                if (listeners != null) {
                    listeners.remove(listenerWithFilter);
                }
                listenerToSessionMap.remove(listener);
                removeSubscriptionForSession(this, subscriptionSessionId);
            }
        };
        addSubscriptionForSession(subscription, subscriptionSessionId);
        return subscription;
    }

    public  Subscription subscribe(final Topic topic, final MessageListener listener) {
        return subscribe(topic, listener, null);
    }

    protected  void triggerEventHandling(final DolphinEvent event) {
        Assert.requireNonNull(event, "event");

        final Topic topic = event.getMessageEventContext().getTopic();
        LOG.trace("Handling data for topic {}", topic.getName());
        final List> listeners = topicToListenerMap.get(topic);
        if (listeners != null) {
            for (final ListenerWithFilter listenerAndFilter : listeners) {
                final String sessionId = listenerToSessionMap.get(listenerAndFilter.getListener());
                if (sessionId == null) {
                    throw new RuntimeException("Internal Error! No session id defined for event bus listener!");
                }
                if (sendInSameClientSession(event, (MessageListener) listenerAndFilter.getListener())) {
                    // This listener was already called at the publish call
                    // since the event was called from the same session
                    LOG.trace("Event listener for topic {} was already called in Dolphin Platform context {}", topic.getName(), sessionId);
                } else {
                    LOG.trace("Event listener for topic {} must be called later in Dolphin Platform context {}", topic.getName(), sessionId);
                    contextProvider.getContextById(sessionId).runLater(new Runnable() {

                        @Override
                        public void run() {
                            LOG.trace("Calling event listener for topic {} in Dolphin Platform context {}", topic.getName(), sessionId);
                            final Predicate> sessionFilter = ((ListenerWithFilter) listenerAndFilter).getFilter();
                            final MessageListener listener = (MessageListener) listenerAndFilter.getListener();
                            try {
                                if (sessionFilter == null || sessionFilter.test(event.getMessageEventContext())) {
                                    listener.onMessage(event);
                                }
                            } catch (final Exception e) {
                                LOG.error("Error in calling event listener for topic '" + topic.getName() + "' in Dolphin Platform context " + sessionId, e);
                            }
                        }
                    });
                }
            }
        }
    }

    private  boolean sendInSameClientSession(final DolphinEvent event, final MessageListener listener) {
        Assert.requireNonNull(event, "event");
        final String listenerSessionId = listenerToSessionMap.get(listener);
        final MessageEventContext eventContext = event.getMessageEventContext();
        if (eventContext != null) {
            return new ClientSessionEventFilter(listenerSessionId).test(eventContext);
        }
        return false;
    }

    protected abstract  void publishForOtherSessions(final DolphinEvent event);

    private void checkInitialization() {
        if (!initialized.get()) {
            throw new RuntimeException("EventBus not initialized");
        }
    }

    private  List> getListenersForSessionAndTopic(final String sessionId, final Topic topic) {
        Assert.requireNonBlank(sessionId, "sessionId");
        Assert.requireNonNull(topic, "topic");

        final List> handlers = topicToListenerMap.get(topic);
        if (handlers == null) {
            return Collections.emptyList();
        }

        final List> ret = new ArrayList<>();
        for (ListenerWithFilter listener : handlers) {
            if (sessionId.equals(listenerToSessionMap.get(listener.getListener()))) {
                ret.add((ListenerWithFilter) listener);
            }
        }
        return ret;
    }

    private void addSubscriptionForSession(final Subscription subscription, final String dolphinSessionId) {
        List subscriptionsForSession = sessionStore.get(dolphinSessionId);
        if (subscriptionsForSession == null) {
            subscriptionsForSession = new CopyOnWriteArrayList<>();
            sessionStore.put(dolphinSessionId, subscriptionsForSession);
        }
        subscriptionsForSession.add(subscription);
    }

    private void removeSubscriptionForSession(final Subscription subscription, final String dolphinSessionId) {
        final List subscriptionsForSession = sessionStore.get(dolphinSessionId);
        if (subscriptionsForSession != null) {
            subscriptionsForSession.remove(subscription);
        }
    }

    private void onSessionEnds(final String dolphinSessionId) {
        Assert.requireNonBlank(dolphinSessionId, "dolphinSessionId");
        final List subscriptions = sessionStore.get(dolphinSessionId);
        if (subscriptions != null) {
            for (Subscription subscription : subscriptions) {
                subscription.unsubscribe();
            }
        }
    }

    private DolphinContext getCurrentContext() {
        return contextProvider.getCurrentDolphinContext();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy