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

com.pusher.client.channel.impl.ChannelImpl Maven / Gradle / Ivy

There is a newer version: 2.4.4
Show newest version
package com.pusher.client.channel.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import com.google.gson.Gson;

import com.google.gson.GsonBuilder;
import com.pusher.client.channel.*;
import com.pusher.client.util.Factory;

public class ChannelImpl implements InternalChannel {
    private final Gson GSON;
    private static final String INTERNAL_EVENT_PREFIX = "pusher_internal:";
    protected static final String SUBSCRIPTION_SUCCESS_EVENT = "pusher_internal:subscription_succeeded";
    protected final String name;
    private final Map> eventNameToListenerMap = new HashMap>();
    protected volatile ChannelState state = ChannelState.INITIAL;
    private ChannelEventListener eventListener;
    private final Factory factory;
    private final Object lock = new Object();

    public ChannelImpl(final String channelName, final Factory factory) {
        GsonBuilder gsonBldr = new GsonBuilder();
        gsonBldr.registerTypeAdapter(PusherEvent.class, new PusherEventDeserializer());
        GSON = gsonBldr.create();
        if (channelName == null) {
            throw new IllegalArgumentException("Cannot subscribe to a channel with a null name");
        }

        for (final String disallowedPattern : getDisallowedNameExpressions()) {
            if (channelName.matches(disallowedPattern)) {
                throw new IllegalArgumentException(
                        "Channel name "
                                + channelName
                                + " is invalid. Private channel names must start with \"private-\" and presence channel names must start with \"presence-\"");
            }
        }

        name = channelName;
        this.factory = factory;
    }

    /* Channel implementation */

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void bind(final String eventName, final SubscriptionEventListener listener) {

        validateArguments(eventName, listener);

        synchronized (lock) {
            Set listeners = eventNameToListenerMap.get(eventName);
            if (listeners == null) {
                listeners = new HashSet();
                eventNameToListenerMap.put(eventName, listeners);
            }
            listeners.add(listener);
        }
    }

    @Override
    public void unbind(final String eventName, final SubscriptionEventListener listener) {

        validateArguments(eventName, listener);

        synchronized (lock) {
            final Set listeners = eventNameToListenerMap.get(eventName);
            if (listeners != null) {
                listeners.remove(listener);
                if (listeners.isEmpty()) {
                    eventNameToListenerMap.remove(eventName);
                }
            }
        }
    }

    @Override
    public boolean isSubscribed() {
        return state == ChannelState.SUBSCRIBED;
    }

    /* InternalChannel implementation */

    @Override
    public void onMessage(final String event, final String message) {

        if (event.equals(SUBSCRIPTION_SUCCESS_EVENT)) {
            updateState(ChannelState.SUBSCRIBED);
        }
        else {
            final Set listeners;
            synchronized (lock) {
                final Set sharedListeners = eventNameToListenerMap.get(event);
                if (sharedListeners != null) {
                    listeners = new HashSet(sharedListeners);
                }
                else {
                    listeners = null;
                }
            }

            if (listeners != null) {
                for (final SubscriptionEventListener listener : listeners) {
                    final PusherEvent e = GSON.fromJson(message, PusherEvent.class);
                    factory.queueOnEventThread(new Runnable() {
                        @Override
                        public void run() {
                            listener.onEvent(e);
                        }
                    });
                }
            }
        }
    }


    @Override
    public String toSubscribeMessage() {

        final Map jsonObject = new LinkedHashMap();
        jsonObject.put("event", "pusher:subscribe");

        final Map dataMap = new LinkedHashMap();
        dataMap.put("channel", name);

        jsonObject.put("data", dataMap);

        return GSON.toJson(jsonObject);
    }

    @Override
    public String toUnsubscribeMessage() {
        final Map jsonObject = new LinkedHashMap();
        jsonObject.put("event", "pusher:unsubscribe");

        final Map dataMap = new LinkedHashMap();
        dataMap.put("channel", name);

        jsonObject.put("data", dataMap);

        return GSON.toJson(jsonObject);
    }

    @Override
    public void updateState(final ChannelState state) {

        this.state = state;

        if (state == ChannelState.SUBSCRIBED && eventListener != null) {
            factory.queueOnEventThread(new Runnable() {
                @Override
                public void run() {
                    eventListener.onSubscriptionSucceeded(ChannelImpl.this.getName());
                }
            });
        }
    }

    /* Comparable implementation */

    @Override
    public void setEventListener(final ChannelEventListener listener) {
        eventListener = listener;
    }

    @Override
    public ChannelEventListener getEventListener() {
        return eventListener;
    }

    @Override
    public int compareTo(final InternalChannel other) {
        return getName().compareTo(other.getName());
    }

    /* implementation detail */

    @Override
    public String toString() {
        return String.format("[Public Channel: name=%s]", name);
    }


    protected String[] getDisallowedNameExpressions() {
        return new String[] { "^private-.*", "^presence-.*" };
    }

    private void validateArguments(final String eventName, final SubscriptionEventListener listener) {

        if (eventName == null) {
            throw new IllegalArgumentException("Cannot bind or unbind to channel " + name + " with a null event name");
        }

        if (listener == null) {
            throw new IllegalArgumentException("Cannot bind or unbind to channel " + name + " with a null listener");
        }

        if (eventName.startsWith(INTERNAL_EVENT_PREFIX)) {
            throw new IllegalArgumentException("Cannot bind or unbind channel " + name
                    + " with an internal event name such as " + eventName);
        }

        if (state == ChannelState.UNSUBSCRIBED) {
            throw new IllegalStateException(
                    "Cannot bind or unbind to events on a channel that has been unsubscribed. Call Pusher.subscribe() to resubscribe to this channel");
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy