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

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

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

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
import com.pusher.client.AuthorizationFailureException;
import com.pusher.client.Authorizer;
import com.pusher.client.channel.ChannelEventListener;
import com.pusher.client.channel.PresenceChannel;
import com.pusher.client.channel.PresenceChannelEventListener;
import com.pusher.client.channel.SubscriptionEventListener;
import com.pusher.client.channel.User;
import com.pusher.client.connection.impl.InternalConnection;
import com.pusher.client.util.Factory;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class PresenceChannelImpl extends PrivateChannelImpl implements PresenceChannel {

    private static final String MEMBER_ADDED_EVENT = "pusher_internal:member_added";
    private static final String MEMBER_REMOVED_EVENT = "pusher_internal:member_removed";
    private static final Gson GSON = new Gson();

    private final Map idToUserMap = Collections.synchronizedMap(new LinkedHashMap());

    private String myUserID;

    public PresenceChannelImpl(final InternalConnection connection, final String channelName,
            final Authorizer authorizer, final Factory factory) {
        super(connection, channelName, authorizer, factory);
    }

    /* PresenceChannel implementation */

    @Override
    public Set getUsers() {
        return new LinkedHashSet(idToUserMap.values());
    }

    @Override
    public User getMe() {
        return idToUserMap.get(myUserID);
    }

    /* Base class overrides */

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

        super.onMessage(event, message);

        if (event.equals(SUBSCRIPTION_SUCCESS_EVENT)) {
            handleSubscriptionSuccessfulMessage(message);
        }
        else if (event.equals(MEMBER_ADDED_EVENT)) {
            handleMemberAddedEvent(message);
        }
        else if (event.equals(MEMBER_REMOVED_EVENT)) {
            handleMemberRemovedEvent(message);
        }
    }

    @Override
    public String toSubscribeMessage() {
        String msg = super.toSubscribeMessage();
        myUserID = extractUserIdFromChannelData(channelData);
        return msg;
    }

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

        if (listener instanceof PresenceChannelEventListener == false) {
            throw new IllegalArgumentException(
                    "Only instances of PresenceChannelEventListener can be bound to a presence channel");
        }

        super.bind(eventName, listener);
    }

    @Override
    protected String[] getDisallowedNameExpressions() {
        return new String[] { "^(?!presence-).*" };
    }

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

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void handleSubscriptionSuccessfulMessage(final String message) {

        // extract data from the JSON message
        final PresenceData presenceData = extractPresenceDataFrom(message);
        final List ids = presenceData.ids;
        final Map hash = presenceData.hash;

        if (ids != null && !ids.isEmpty()) {
            // build the collection of Users
            for (final String id : ids) {
                final String userData = hash.get(id) != null ? GSON.toJson(hash.get(id)) : null;
                final User user = new User(id, userData);
                idToUserMap.put(id, user);
            }
        }
        final ChannelEventListener listener = getEventListener();
        if (listener != null) {
            final PresenceChannelEventListener presenceListener = (PresenceChannelEventListener)listener;
            presenceListener.onUsersInformationReceived(getName(), getUsers());
        }
    }

    @SuppressWarnings("rawtypes")
    private void handleMemberAddedEvent(final String message) {
        final String dataString = extractDataStringFrom(message);
        MemberData memberData = GSON.fromJson(dataString, MemberData.class);


        final String id = memberData.userId;
        final String userData = memberData.userInfo!= null ? GSON.toJson(memberData.userInfo) : null;

        final User user = new User(id, userData);
        idToUserMap.put(id, user);

        final ChannelEventListener listener = getEventListener();
        if (listener != null) {
            final PresenceChannelEventListener presenceListener = (PresenceChannelEventListener)listener;
            presenceListener.userSubscribed(getName(), user);
        }
    }

    @SuppressWarnings("rawtypes")
    private void handleMemberRemovedEvent(final String message) {

        final String dataString = extractDataStringFrom(message);
        final MemberData memberData = GSON.fromJson(dataString, MemberData.class);

        final User user = idToUserMap.remove(memberData.userId);

        final ChannelEventListener listener = getEventListener();
        if (listener != null) {
            final PresenceChannelEventListener presenceListener = (PresenceChannelEventListener)listener;
            presenceListener.userUnsubscribed(getName(), user);
        }
    }

    @SuppressWarnings("rawtypes")
    private static String extractDataStringFrom(final String message) {
        final Map jsonObject = GSON.fromJson(message, Map.class);
        return  (String) jsonObject.get("data");
    }

    @SuppressWarnings("rawtypes")
    private static PresenceData extractPresenceDataFrom(final String message) {
        final String dataString = extractDataStringFrom(message);
        return GSON.fromJson(dataString, Presence.class).presence;
    }

    @SuppressWarnings("rawtypes")
    private String extractUserIdFromChannelData(final String channelData) {
        final Map channelDataMap;
        try {
            channelDataMap = GSON.fromJson((String)channelData, Map.class);
        } catch (final JsonSyntaxException e) {
            throw new AuthorizationFailureException("Invalid response from Authorizer: unable to parse channel_data object: " + channelData, e);
        }
        Object maybeUserId;
        try {
            maybeUserId = channelDataMap.get("user_id");
        } catch (final NullPointerException e) {
            throw new AuthorizationFailureException("Invalid response from Authorizer: no user_id key in channel_data object: " + channelData);
        }
        if (maybeUserId == null) {
            throw new AuthorizationFailureException("Invalid response from Authorizer: no user_id key in channel_data object: " + channelData);
        }
        // user_id can be a string or an integer in the Channels websocket protocol
        return String.valueOf(maybeUserId);
    }

    private class MemberData {
        @SerializedName("user_id")
        public String userId;
        @SerializedName("user_info")
        public Object userInfo;
    }

    private class PresenceData {
        @SerializedName("count")
        public Integer count;
        @SerializedName("ids")
        public List ids;
        @SerializedName("hash")
        public Map hash;
    }

    private class Presence {
        @SerializedName("presence")
        public PresenceData presence;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy