com.leacox.pusherclient.PusherClient.java Source code

Java tutorial

Introduction

Here is the source code for com.leacox.pusherclient.PusherClient.java

Source

package com.leacox.pusherclient;

/*   
 *  Copyright (C) 2012 Justin Schultz
 *  JavaPusherClient, a Pusher (http://pusherapp.com) client for Java
 *  
 *  http://justinschultz.com/
 *  http://publicstaticdroidmain.com/
 * 
 *  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. 
 */

import java.net.URI;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.leacox.websocket.WebSocket;
import com.leacox.websocket.WebSocketConnection;
import com.leacox.websocket.WebSocketEventHandler;
import com.leacox.websocket.WebSocketException;
import com.leacox.websocket.WebSocketMessage;

public class PusherClient {
    private static final Logger logger = LoggerFactory.getLogger(PusherClient.class);
    private static final String PUSHER_CLIENT = "java-android-client";
    private static final String VERSION = "1.11";
    private static final String PROTOCOL_VERSION = "5";
    private static final String HOST = "ws.pusherapp.com";
    private static final int WS_PORT = 80;
    private static final int WSS_PORT = 443;
    private static final String WS_PREFIX = "ws://";
    private static final String WSS_PREFIX = "wss://";

    private WebSocket webSocket;
    private String socketId;
    private boolean allowAutoReconnect = true;
    private final String apiKey;
    private final HashMap<String, Channel> channels;
    private final boolean isEncrypted;
    private final ConnectionTimeoutManager timeoutManager;

    private PusherListener pusherEventListener;
    private Timer activityTimer;

    public PusherClient(String key, boolean isEncrypted) {
        this.apiKey = key;
        this.channels = new HashMap<String, Channel>();
        this.isEncrypted = isEncrypted;
        this.timeoutManager = new ConnectionTimeoutManager(this);
    }

    public void connect() {
        String path = "/app/" + apiKey + "?client=" + PUSHER_CLIENT + "&version=" + VERSION + "protocol="
                + PROTOCOL_VERSION;
        allowAutoReconnect = true;

        try {
            URI url = new URI(getPrefix() + HOST + ":" + getPort() + path);
            webSocket = new WebSocketConnection(url);
            webSocket.setEventHandler(new WebSocketEventHandler() {
                @Override
                public void onOpen() {
                    // Pusher's onOpen is invoked after we've received a
                    // socket_id in onMessage()
                }

                @Override
                public void onMessage(WebSocketMessage message) {
                    try {
                        resetActivityTimer();

                        JSONObject jsonMessage = new JSONObject(message.getText());
                        String event = jsonMessage.optString("event", null);

                        if (event.equals("pusher:connection_established")) {
                            timeoutManager.stopTimeoutTimer();

                            JSONObject data = new JSONObject(jsonMessage.getString("data"));
                            socketId = data.getString("socket_id");
                            pusherEventListener.onConnect(socketId);
                        } else if (event.equals("pusher:ping")) {
                            send("pusher:pong", new JSONObject());
                        } else {
                            pusherEventListener.onMessage(jsonMessage.toString());
                            dispatchChannelEvent(jsonMessage, event);
                        }
                    } catch (Exception e) {
                        logger.error("Error receiving message", e);
                    }
                }

                @Override
                public void onClose() {
                    logger.info("Pusher socket closed");
                    if (allowAutoReconnect) {
                        timeoutManager.retryConnect();
                    } else {
                        pusherEventListener.onDisconnect();
                    }
                }
            });

            webSocket.connect();
            timeoutManager.startTimeoutTimer();
        } catch (Exception e) {
            logger.error("Error connecting", e);
        }
    }

    public void disconnect() {
        try {
            allowAutoReconnect = false;
            webSocket.close();
        } catch (Exception e) {
            logger.error("Error closing socket", e);
        }
    }

    public boolean isConnected() {
        return webSocket != null && webSocket.isConnected();
    }

    public void setPusherListener(PusherListener listener) {
        pusherEventListener = listener;
    }

    public Channel subscribe(String channelName) {
        Channel c = new Channel(channelName);

        if (webSocket != null && webSocket.isConnected()) {
            try {
                sendSubscribeMessage(c);
            } catch (Exception e) {
                logger.error("error subscribing", e);
            }
        }

        channels.put(channelName, c);
        return c;
    }

    /**
     * Subscribes to the specified channel and uses the specified authorizor for getting an authorization token.
     * 
     * @param channelName
     *            The name of the channel to subscribe to.
     * @param authorizor
     *            The authorizor to use for getting an authorization token.
     * @return The subscribed channel
     */
    public Channel subscribe(String channelName, PusherAuthorizor authorizor) {
        Channel c = new Channel(channelName);

        if (webSocket != null && webSocket.isConnected()) {
            try {
                String authToken = authorizor.authorize(socketId, channelName);
                sendSubscribeMessage(c, authToken);
            } catch (Exception e) {
                logger.error("Error subscribing", e);
            }
        }

        channels.put(channelName, c);
        return c;
    }

    public Channel subscribe(String channelName, String authToken) {
        Channel c = new Channel(channelName);

        if (webSocket != null && webSocket.isConnected()) {
            try {
                sendSubscribeMessage(c, authToken);
            } catch (Exception e) {
                logger.error("Error subscribing", e);
            }
        }

        channels.put(channelName, c);
        return c;
    }

    public Channel subscribe(String channelName, String authToken, int userId) {
        Channel c = new Channel(channelName);

        if (webSocket != null && webSocket.isConnected()) {
            try {
                sendSubscribeMessage(c, authToken, userId);
            } catch (Exception e) {
                logger.error("Error subscribing", e);
            }
        }

        channels.put(channelName, c);
        return c;
    }

    public void unsubscribe(String channelName) {
        if (channels.containsKey(channelName)) {
            if (webSocket != null && webSocket.isConnected()) {
                try {
                    sendUnsubscribeMessage(channels.get(channelName));
                } catch (Exception e) {
                    logger.error("Error unsubscribing", e);
                }
            }

            channels.remove(channelName);
        }
    }

    private void sendSubscribeMessage(Channel c) {
        JSONObject data = new JSONObject();
        c.send("pusher:subscribe", data);
    }

    private void sendSubscribeMessage(Channel c, String authToken) {
        JSONObject data = new JSONObject();
        try {
            data.put("auth", authToken);
        } catch (Exception e) {
            logger.error("Error sending subscribe message", e);
        }

        c.send("pusher:subscribe", data);
    }

    private void sendSubscribeMessage(Channel c, String authToken, int userId) {
        JSONObject data = new JSONObject();
        try {
            data.put("auth", authToken);
            data.put("channel_data", new JSONObject().put("user_id", userId));
        } catch (Exception e) {
            logger.error("Error sending subscribe message", e);
        }

        c.send("pusher:subscribe", data);
    }

    private void sendUnsubscribeMessage(Channel c) {
        JSONObject data = new JSONObject();
        c.send("pusher:unsubscribe", data);
    }

    private void dispatchChannelEvent(JSONObject jsonMessage, String event) {
        String channelName = jsonMessage.optString("channel", null);

        Channel channel = channels.get(channelName);
        if (channel != null) {
            ChannelListener channelListener = channel.channelEvents.get(event);

            if (channelListener != null)
                channelListener.onMessage(jsonMessage.toString());
        }
    }

    public void send(String event_name, JSONObject data) {
        JSONObject message = new JSONObject();

        try {
            message.put("event", event_name);
            message.put("data", data);
            webSocket.send(message.toString());
        } catch (Exception e) {
            logger.error("Error sending message", e);
        }
    }

    public class Channel {
        private final String channelName;
        private final HashMap<String, ChannelListener> channelEvents;

        public Channel(String _name) {
            channelName = _name;
            channelEvents = new HashMap<String, ChannelListener>();
        }

        public void send(String eventName, JSONObject data) {
            JSONObject message = new JSONObject();

            try {
                data.put("channel", channelName);
                message.put("event", eventName);
                message.put("data", data);
                webSocket.send(message.toString());
            } catch (Exception e) {
                logger.error("Error sending message", e);
            }
        }

        public void bind(String eventName, ChannelListener channelListener) {
            channelEvents.put(eventName, channelListener);
        }

        @Override
        public String toString() {
            return channelName;
        }
    }

    private String getPrefix() {
        return isEncrypted ? WSS_PREFIX : WS_PREFIX;
    }

    private int getPort() {
        return isEncrypted ? WSS_PORT : WS_PORT;
    }

    private void resetActivityTimer() {
        if (activityTimer != null) {
            activityTimer.cancel();
        }

        activityTimer = new Timer();
        activityTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                logger.debug("Sending PING");
                send("pusher:ping", new JSONObject());
                activityTimer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        try {
                            logger.debug("Did not receive PONG: Closing connection");
                            webSocket.close();
                        } catch (WebSocketException e) {
                            logger.error("Error closing web socket connection", e);
                        }
                    }
                }, 30000);
            }
        }, 120000);
    }
}