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

org.cometd.server.ServerChannelImpl Maven / Gradle / Ivy

There is a newer version: 8.0.6
Show newest version
/*
 * Copyright (c) 2008-2018 the original author or authors.
 *
 * 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 org.cometd.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.cometd.bayeux.ChannelId;
import org.cometd.bayeux.Promise;
import org.cometd.bayeux.Session;
import org.cometd.bayeux.server.Authorizer;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.LocalSession;
import org.cometd.bayeux.server.ServerChannel;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServerChannelImpl implements ServerChannel, Dumpable {
    private static final Logger _logger = LoggerFactory.getLogger(ServerChannel.class);
    private final BayeuxServerImpl _bayeux;
    private final ChannelId _id;
    private final AttributesMap _attributes = new AttributesMap();
    private final Set _subscribers = new CopyOnWriteArraySet<>();
    private final List _listeners = new CopyOnWriteArrayList<>();
    private final List _authorizers = new CopyOnWriteArrayList<>();
    private final CountDownLatch _initialized = new CountDownLatch(1);
    private final AtomicInteger _sweeperPasses = new AtomicInteger();
    private boolean _lazy;
    private long _lazyTimeout = -1;
    private boolean _persistent;

    protected ServerChannelImpl(BayeuxServerImpl bayeux, ChannelId id) {
        _bayeux = bayeux;
        _id = id;
        setPersistent(!isBroadcast());
    }

    /**
     * Waits for the channel to be {@link #initialized() initialized}, to avoid
     * that channels are returned to applications in a half-initialized state,
     * in particular before {@link Initializer}s have run.
     *
     * @see BayeuxServerImpl#createChannelIfAbsent(String, Initializer...)
     */
    void waitForInitialized() {
        try {
            if (!_initialized.await(5, TimeUnit.SECONDS)) {
                throw new IllegalStateException("Not Initialized: " + this);
            }
        } catch (InterruptedException x) {
            throw new IllegalStateException("Initialization interrupted: " + this, x);
        }
    }

    /**
     * Marks this channel as initialized, notifying other threads that may
     * {@link #waitForInitialized() wait for initialization}.
     */
    void initialized() {
        resetSweeperPasses();
        _initialized.countDown();
    }

    void resetSweeperPasses() {
        _sweeperPasses.set(0);
    }

    @Override
    public boolean subscribe(ServerSession session) {
        return subscribe((ServerSessionImpl)session, null);
    }

    protected boolean subscribe(ServerSessionImpl session, ServerMessage message) {
        if (isService()) {
            // Subscription to service channels is a no operation.
            return true;
        }

        if (isMeta()) {
            return false;
        }

        resetSweeperPasses();

        if (session.subscribe(this)) {
            if (_subscribers.add(session)) {
                for (ServerChannelListener listener : _listeners) {
                    if (listener instanceof SubscriptionListener) {
                        notifySubscribed((SubscriptionListener)listener, session, this, message);
                    }
                }
                for (BayeuxServer.BayeuxServerListener listener : _bayeux.getListeners()) {
                    if (listener instanceof BayeuxServer.SubscriptionListener) {
                        notifySubscribed((BayeuxServer.SubscriptionListener)listener, session, this, message);
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }

    private void notifySubscribed(SubscriptionListener listener, ServerSession session, ServerChannel channel, ServerMessage message) {
        try {
            listener.subscribed(session, channel, message);
        } catch (Throwable x) {
            _logger.info("Exception while invoking listener " + listener, x);
        }
    }

    private void notifySubscribed(BayeuxServer.SubscriptionListener listener, ServerSession session, ServerChannel channel, ServerMessage message) {
        try {
            listener.subscribed(session, channel, message);
        } catch (Throwable x) {
            _logger.info("Exception while invoking listener " + listener, x);
        }
    }

    @Override
    public boolean unsubscribe(ServerSession session) {
        return unsubscribe((ServerSessionImpl)session, null);
    }

    protected boolean unsubscribe(ServerSessionImpl session, ServerMessage message) {
        // The unsubscription may arrive when the session
        // is already disconnected; unsubscribe in any case

        // Subscriptions to service channels are allowed but
        // are a no-operation, so be symmetric here
        if (isService()) {
            return true;
        }
        if (isMeta()) {
            return false;
        }

        if (_subscribers.remove(session)) {
            session.unsubscribedFrom(this);
            for (ServerChannelListener listener : _listeners) {
                if (listener instanceof SubscriptionListener) {
                    notifyUnsubscribed((SubscriptionListener)listener, session, this, message);
                }
            }
            for (BayeuxServer.BayeuxServerListener listener : _bayeux.getListeners()) {
                if (listener instanceof BayeuxServer.SubscriptionListener) {
                    notifyUnsubscribed((BayeuxServer.SubscriptionListener)listener, session, this, message);
                }
            }
        }

        return true;
    }

    private void notifyUnsubscribed(BayeuxServer.SubscriptionListener listener, ServerSession session, ServerChannel channel, ServerMessage message) {
        try {
            listener.unsubscribed(session, channel, message);
        } catch (Throwable x) {
            _logger.info("Exception while invoking listener " + listener, x);
        }
    }

    private void notifyUnsubscribed(SubscriptionListener listener, ServerSession session, ServerChannel channel, ServerMessage message) {
        try {
            listener.unsubscribed(session, channel, message);
        } catch (Throwable x) {
            _logger.info("Exception while invoking listener " + listener, x);
        }
    }

    @Override
    public Set getSubscribers() {
        return Collections.unmodifiableSet(subscribers());
    }

    public Set subscribers() {
        return _subscribers;
    }

    @Override
    public boolean isBroadcast() {
        return !isMeta() && !isService();
    }

    @Override
    public boolean isDeepWild() {
        return _id.isDeepWild();
    }

    @Override
    public boolean isLazy() {
        return _lazy;
    }

    @Override
    public boolean isPersistent() {
        return _persistent;
    }

    @Override
    public boolean isWild() {
        return _id.isWild();
    }

    @Override
    public void setLazy(boolean lazy) {
        _lazy = lazy;
        if (!lazy) {
            _lazyTimeout = -1;
        }
    }

    @Override
    public long getLazyTimeout() {
        return _lazyTimeout;
    }

    @Override
    public void setLazyTimeout(long lazyTimeout) {
        _lazyTimeout = lazyTimeout;
        setLazy(lazyTimeout > 0);
    }

    @Override
    public void setPersistent(boolean persistent) {
        resetSweeperPasses();
        _persistent = persistent;
    }

    @Override
    public void addListener(ServerChannelListener listener) {
        resetSweeperPasses();
        _listeners.add(listener);
    }

    @Override
    public void removeListener(ServerChannelListener listener) {
        _listeners.remove(listener);
    }

    @Override
    public List getListeners() {
        return Collections.unmodifiableList(listeners());
    }

    protected List listeners() {
        return _listeners;
    }

    @Override
    public ChannelId getChannelId() {
        return _id;
    }

    @Override
    public String getId() {
        return _id.getId();
    }

    @Override
    public boolean isMeta() {
        return _id.isMeta();
    }

    @Override
    public boolean isService() {
        return _id.isService();
    }

    @Override
    public void publish(Session from, ServerMessage.Mutable mutable, Promise promise) {
        if (isWild()) {
            throw new IllegalStateException("Wild publish");
        }

        mutable.setChannel(getId());

        ServerSessionImpl session = null;
        if (from instanceof ServerSessionImpl) {
            session = (ServerSessionImpl)from;
        } else if (from instanceof LocalSession) {
            session = (ServerSessionImpl)((LocalSession)from).getServerSession();
        }

        _bayeux.publish(session, this, mutable, false, promise);
    }

    @Override
    public void publish(Session from, Object data, Promise promise) {
        ServerMessage.Mutable mutable = _bayeux.newMessage();
        mutable.setData(data);
        publish(from, mutable, promise);
    }

    protected void sweep() {
        waitForInitialized();

        for (ServerSession session : _subscribers) {
            if (!session.isHandshook()) {
                unsubscribe(session);
            }
        }

        if (isMeta() || isPersistent()) {
            return;
        }

        if (!_subscribers.isEmpty()) {
            return;
        }

        if (!_authorizers.isEmpty()) {
            return;
        }

        for (ServerChannelListener listener : _listeners) {
            if (!(listener instanceof ServerChannelListener.Weak)) {
                return;
            }
        }

        if (_sweeperPasses.incrementAndGet() < 3) {
            return;
        }

        remove();
    }

    @Override
    public void remove() {
        if (_bayeux.removeServerChannel(this)) {
            for (ServerSession subscriber : _subscribers) {
                ((ServerSessionImpl)subscriber).unsubscribedFrom(this);
            }
            _subscribers.clear();
        }

        _listeners.clear();
    }

    @Override
    public void setAttribute(String name, Object value) {
        _attributes.setAttribute(name, value);
    }

    @Override
    public Object getAttribute(String name) {
        return _attributes.getAttribute(name);
    }

    @Override
    public Set getAttributeNames() {
        return _attributes.getAttributeNameSet();
    }

    @Override
    public Object removeAttribute(String name) {
        Object old = getAttribute(name);
        _attributes.removeAttribute(name);
        return old;
    }

    @Override
    public void addAuthorizer(Authorizer authorizer) {
        _authorizers.add(authorizer);
    }

    @Override
    public void removeAuthorizer(Authorizer authorizer) {
        _authorizers.remove(authorizer);
    }

    @Override
    public List getAuthorizers() {
        return Collections.unmodifiableList(authorizers());
    }

    protected List authorizers() {
        return _authorizers;
    }

    @Override
    public String dump() {
        return ContainerLifeCycle.dump(this);
    }

    @Override
    public void dump(Appendable out, String indent) throws IOException {
        ContainerLifeCycle.dumpObject(out, this);

        List children = new ArrayList<>();

        children.add(new Dumpable() {
            @Override
            public String dump() {
                return null;
            }

            @Override
            public void dump(Appendable out, String indent) throws IOException {
                List authorizers = getAuthorizers();
                ContainerLifeCycle.dumpObject(out, "authorizers: " + authorizers.size());
                if (_bayeux.isDetailedDump()) {
                    ContainerLifeCycle.dump(out, indent, authorizers);
                }
            }
        });

        children.add(new Dumpable() {
            @Override
            public String dump() {
                return null;
            }

            @Override
            public void dump(Appendable out, String indent) throws IOException {
                List listeners = getListeners();
                ContainerLifeCycle.dumpObject(out, "listeners: " + listeners.size());
                if (_bayeux.isDetailedDump()) {
                    ContainerLifeCycle.dump(out, indent, listeners);
                }
            }
        });

        children.add(new Dumpable() {
            @Override
            public String dump() {
                return null;
            }

            @Override
            public void dump(Appendable out, String indent) throws IOException {
                Set subscribers = getSubscribers();
                ContainerLifeCycle.dumpObject(out, "subscribers: " + subscribers.size());
                if (_bayeux.isDetailedDump()) {
                    ContainerLifeCycle.dump(out, indent, subscribers);
                }
            }
        });

        ContainerLifeCycle.dump(out, indent, children);
    }

    @Override
    public String toString() {
        return _id.toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy