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) 2010 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.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.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.ConcurrentHashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServerChannelImpl implements ServerChannel
{
    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 final Set _children = new ConcurrentHashSet();
    private final ServerChannelImpl _parent;
    private boolean _lazy;
    private boolean _persistent;

    protected ServerChannelImpl(BayeuxServerImpl bayeux, ChannelId id, ServerChannelImpl parent)
    {
        _bayeux = bayeux;
        _id = id;
        _parent = parent;
        if (parent != null)
            parent.addChild(this);
        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#createIfAbsent(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);
    }

    public boolean subscribe(ServerSession session)
    {
        if (!session.isHandshook())
            return false;

        // Maintain backward compatibility by allowing subscriptions
        // to service channels to be a no-operation, but succeed
        if (isService())
            return true;
        if (isMeta())
            return false;

        return subscribe((ServerSessionImpl)session);
    }

    private boolean subscribe(ServerSessionImpl session)
    {
        resetSweeperPasses();
        if (_subscribers.add(session))
        {
            session.subscribedTo(this);
            for (ServerChannelListener listener : _listeners)
                if (listener instanceof SubscriptionListener)
                    notifySubscribed((SubscriptionListener)listener, session, this);
            for (BayeuxServer.BayeuxServerListener listener : _bayeux.getListeners())
                if (listener instanceof BayeuxServer.SubscriptionListener)
                    notifySubscribed((BayeuxServer.SubscriptionListener)listener, session, this);
        }
        return true;
    }

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

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

    public boolean unsubscribe(ServerSession session)
    {
        // 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;

        return unsubscribe((ServerSessionImpl)session);
    }

    private boolean unsubscribe(ServerSessionImpl session)
    {
        if (_subscribers.remove(session))
        {
            session.unsubscribedFrom(this);
            for (ServerChannelListener listener : _listeners)
                if (listener instanceof SubscriptionListener)
                    notifyUnsubscribed((SubscriptionListener)listener, session, this);
            for (BayeuxServer.BayeuxServerListener listener : _bayeux.getListeners())
                if (listener instanceof BayeuxServer.SubscriptionListener)
                    notifyUnsubscribed((BayeuxServer.SubscriptionListener)listener, session, this);
        }
        return true;
    }

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

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

    public Set getSubscribers()
    {
        return Collections.unmodifiableSet(_subscribers);
    }

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

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

    public boolean isLazy()
    {
        return _lazy;
    }

    public boolean isPersistent()
    {
        return _persistent;
    }

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

    public void setLazy(boolean lazy)
    {
        _lazy = lazy;
    }

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

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

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

    public List getListeners()
    {
        return Collections.unmodifiableList(_listeners);
    }

    public ChannelId getChannelId()
    {
        return _id;
    }

    public String getId()
    {
        return _id.toString();
    }

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

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

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

        ServerSessionImpl session = (from instanceof ServerSessionImpl)
                ? (ServerSessionImpl)from
                : ((from instanceof LocalSession) ? (ServerSessionImpl)((LocalSession)from).getServerSession() : null);

        // Do not leak the clientId to other subscribers
        // as we are now "sending" this message
        mutable.setClientId(null);

        if (_bayeux.extendSend(session, null, mutable))
            _bayeux.doPublish(session, this, mutable);
    }

    public void publish(Session from, Object data, String id)
    {
        ServerMessage.Mutable mutable = _bayeux.newMessage();
        mutable.setChannel(getId());
        if (from != null)
            mutable.setClientId(from.getId());
        mutable.setData(data);
        mutable.setId(id);
        publish(from, mutable);
    }

    protected void sweep()
    {
        waitForInitialized();

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

        if (isPersistent())
            return;

        if (_subscribers.size() > 0)
            return;

        if (_authorizers.size() > 0)
            return;

        if (!isWild())
        {
            // Not wild, then check if it has children
            if (_children.size() > 0)
                return;
        }

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

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

        remove();
    }

    public void remove()
    {
        if (_parent != null)
            _parent.removeChild(this);

        for (ServerChannelImpl child : _children)
            child.remove();

        if (_bayeux.removeServerChannel(this))
        {
            for (ServerSession subscriber : _subscribers)
                ((ServerSessionImpl)subscriber).unsubscribedFrom(this);
            _subscribers.clear();
        }

        _listeners.clear();
    }

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

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

    public Set getAttributeNames()
    {
        return _attributes.keySet();
    }

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

    private void addChild(ServerChannelImpl child)
    {
        _children.add(child);
    }

    private void removeChild(ServerChannelImpl child)
    {
        _children.remove(child);
    }

    protected void dump(StringBuilder b, String indent)
    {
        b.append(toString());
        b.append(isLazy() ? " lazy" : "");
        b.append('\n');

        int leaves = _children.size() + _subscribers.size() + _listeners.size() + _authorizers.size();
        int i = 0;
        for (ServerChannelImpl child : _children)
        {
            b.append(indent);
            b.append(" +-");
            child.dump(b, indent + ((++i == leaves) ? "   " : " | "));
        }
        for (ServerSession child : _subscribers)
        {
            b.append(indent);
            b.append(" +-");
            ((ServerSessionImpl)child).dump(b, indent + ((++i == leaves) ? "   " : " | "));
        }
        for (ServerChannelListener child : _listeners)
        {
            b.append(indent);
            b.append(" +-");
            b.append(child);
            b.append('\n');
        }
        for (Authorizer auth : _authorizers)
        {
            b.append(indent);
            b.append(" +-");
            b.append(auth);
            b.append('\n');
        }
    }

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

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

    public List getAuthorizers()
    {
        return Collections.unmodifiableList(_authorizers);
    }

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy