itcc.li.vereinsaustausch.fcmServerComponent.VereinsaustauschFcmServer.java Source code

Java tutorial

Introduction

Here is the source code for itcc.li.vereinsaustausch.fcmServerComponent.VereinsaustauschFcmServer.java

Source

/**
 * Copyright 2015 Google Inc. All Rights Reserved.
 * Modifications copyright 2016 Thomas Elsensohn. All Rights Reserved.
    
 * 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 itcc.li.vereinsaustausch.fcmServerComponent;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jivesoftware.smack.util.StringUtils;

import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;

public class VereinsaustauschFcmServer extends AbstractGcmServer {
    // Keys
    private static final String ACTION_KEY = "action";
    private static final String CLIENT_KEY = "client";
    private static final String CLIENTS_KEY = "clients";
    private static final String DATA_KEY = "data";

    // Actions
    private static final String REGISTER_NEW_CLIENT = "register_new_client";
    private static final String SEND_CLIENT_LIST = "send_client_list";
    private static final String CLICK_ACTION = "ping_received";
    private static final String BROADCAST_NEW_CLIENT = "broadcast_new_client";
    private static final String NEW_CLIENT_TOPIC = "/topics/newclient";
    private static final String PING_CLIENT = "ping_client";
    private static final String PING_ICON = "mipmap/ic_launcher";
    private static final String PING_TITLE = "Friendly Ping!";
    private static final String NEW_MESSAGE = "new_message";

    private static final String SENDER_ID = "596165323328";
    private static final String SENDER_KEY = "AIzaSyB5FL58bVV3pZ5RiLe3pHyZtrWPl4eJG2U";
    public static final String SERVICE_NAME = "Vereinsaustausch Fcm-Server";

    // Store of clients registered with VereinsaustauschFcmServer.
    private Map<String, ClientModel> clientMap;
    static final Logger logger = Logger.getLogger("VereinsaustauschFcmServer");

    // Gson helper to assist with going to and from JSON and Client.
    private Gson gson;
    private Query dbLastChatEntry;
    private ChildEventListener messageListener;
    private boolean firstInitial = true;

    public VereinsaustauschFcmServer(String apiKey, String senderId) {
        super(apiKey, senderId, SERVICE_NAME);
        clientMap = new ConcurrentHashMap<String, ClientModel>();

        ClientModel serverClient = createServerClient();
        clientMap.put(serverClient.registrationToken, serverClient);

        gson = new GsonBuilder().create();

        dbLastChatEntry = FirebaseDatabase.getInstance().getReference().child("Vereine").child("itcc")
                .child("ChatLog").limitToLast(1);
        messageListener = new ChildEventListener() {

            @Override
            public void onCancelled(DatabaseError arg0) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onChildAdded(DataSnapshot arg0, String arg1) {
                if (!firstInitial) {
                    sendNotificationToClients(
                            arg0.getValue(itcc.li.vereinsaustausch.fcmServerComponent.ChatEntry.class).message);
                } else {
                    firstInitial = false;
                }
            }

            @Override
            public void onChildChanged(DataSnapshot arg0, String arg1) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onChildMoved(DataSnapshot arg0, String arg1) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onChildRemoved(DataSnapshot arg0) {
                // TODO Auto-generated method stub

            }

        };

        dbLastChatEntry.addChildEventListener(messageListener);

    }

    /**
     * Add given client to Map of Clients.
     *
     * @param client
     *            Client to be added.
     */
    private void addClient(ClientModel client) {
        clientMap.put(client.registrationToken, client);
    }

    /**
     * Broadcast the newly registered client to clients that have already been
     * registered. The broadcast is sent via the PubSub topic "/topics/newuser"
     * all registered clients should be subscribed to this topic.
     *
     * @param client
     *            Newly registered client.
     */
    private void broadcastNewClient(ClientModel client) {
        JsonObject jBroadcast = new JsonObject();

        JsonObject jData = new JsonObject();
        jData.addProperty(ACTION_KEY, BROADCAST_NEW_CLIENT);

        JsonObject jClient = gson.toJsonTree(client).getAsJsonObject();
        jData.add(CLIENT_KEY, jClient);

        jBroadcast.add(DATA_KEY, jData);
        this.send(NEW_CLIENT_TOPIC, jBroadcast);
    }

    /**
     * Create a Client object to be used in responses to pings to the server.
     *
     * @return Server Client.
     */
    private ClientModel createServerClient() {
        ClientModel client = new ClientModel();
        client.name = "ThomasServer";
        client.registrationToken = SENDER_ID + "@gcm.googleapis.com";
        client.profilePictureUrl = "";
        return client;
    }

    @Override
    public void onMessage(String from, JsonObject jData) {
        if (jData.has("action")) {
            String action = jData.get("action").getAsString();
            if (action.equals(REGISTER_NEW_CLIENT)) {
                registerNewClient(jData);
            } else if (action.equals("ping_client")) {
                String toToken = jData.get("to").getAsString();
                String senderToken = jData.get("sender").getAsString();
                if (StringUtils.isNotEmpty(toToken) && StringUtils.isNotEmpty(senderToken)) {
                    pingClient(toToken, senderToken);
                } else {
                    logger.info("Unable to ping unless to and sender tokens are available.");
                }
            }
        } else {
            logger.info("No action found. Message received missing action.");
        }
    }

    /**
     * Send message to Client with matching toToken. The validity of to and
     * sender tokens should be check before this method is called.
     *
     * @param toToken
     *            Token of recipient of ping.
     * @param senderToken
     *            Token of sender of ping.
     */
    void pingClient(String toToken, String senderToken) {
        ClientModel senderClient;
        // If the server is the recipient of the ping, send ping to sender,
        // otherwise send ping to
        // toToken.
        if (toToken.equals(SENDER_ID + "@" + AbstractGcmServer.GCM_HOST)) {
            senderClient = clientMap.get(toToken);
            toToken = senderToken;
        } else {
            senderClient = clientMap.get(senderToken);
        }
        JsonObject jPing = new JsonObject();

        JsonObject jData = new JsonObject();
        jData.addProperty(ACTION_KEY, PING_CLIENT);
        jData.addProperty(SENDER_KEY, senderClient.registrationToken);

        // Create notification that is handled appropriately on the receiving
        // platform.
        JsonObject jNotification = new JsonObject();
        jNotification.addProperty("body", senderClient.name + " is pinging you.");
        jNotification.addProperty("title", PING_TITLE);
        jNotification.addProperty("icon", PING_ICON);
        jNotification.addProperty("sound", "default");
        jNotification.addProperty("click_action", CLICK_ACTION);

        jPing.add(DATA_KEY, jData);
        jPing.add("notification", jNotification);

        this.send(toToken, jPing);
    }

    /**
     * Create Client from given JSON data, add client to client list, broadcast
     * newly registered client to all previously registered clients and send
     * client list to new client.
     *
     * @param jData
     *            JSON data containing properties of new Client.
     */
    void registerNewClient(JsonObject jData) {
        ClientModel newClient = gson.fromJson(jData, ClientModel.class);
        if (newClient.isValid()) {
            addClient(newClient);
            broadcastNewClient(newClient);
            sendClientList(newClient);
            logger.log(Level.INFO, "Register new client");

        } else {
            logger.log(Level.WARNING, "Could not unpack received data into a Client.");
        }
    }

    /**
     * Send client list to newly registered client. When a new client is
     * registered, that client must be informed about the other registered
     * clients.
     *
     * @param client
     *            Newly registered client.
     */
    private void sendClientList(ClientModel client) {
        ArrayList<ClientModel> clientList = new ArrayList<ClientModel>();
        for (Entry<String, ClientModel> clientEntry : clientMap.entrySet()) {
            ClientModel currentClient = clientEntry.getValue();
            if (currentClient.registrationToken != client.registrationToken) {
                clientList.add(currentClient);
            }
        }
        JsonElement clientElements = gson.toJsonTree(clientList, new TypeToken<Collection<ClientModel>>() {
        }.getType());
        if (clientElements.isJsonArray()) {
            JsonObject jSendClientList = new JsonObject();

            JsonObject jData = new JsonObject();
            jData.addProperty(ACTION_KEY, SEND_CLIENT_LIST);
            jData.add(CLIENTS_KEY, clientElements);

            jSendClientList.add(DATA_KEY, jData);
            this.send(client.registrationToken, jSendClientList);
        }
    }

    private void sendNotificationToClients(String message) {
        JsonObject jPing = new JsonObject();
        JsonObject jData = new JsonObject();
        jData.addProperty(ACTION_KEY, NEW_MESSAGE);
        jData.addProperty(SENDER_KEY, SENDER_ID);

        // Create notification that is handled appropriately on the receiving
        // platform.
        JsonObject jNotification = new JsonObject();
        jNotification.addProperty("body", message);
        jNotification.addProperty("title", "Neue Nachricht");
        jNotification.addProperty("icon", PING_ICON);
        jNotification.addProperty("sound", "default");
        jNotification.addProperty("click_action", CLICK_ACTION);

        jPing.add(DATA_KEY, jData);
        jPing.add("notification", jNotification);

        clientMap.forEach((k, v) -> this.send(k, jPing));
    }

}