de.jackwhite20.japs.client.sub.impl.SubscriberImpl.java Source code

Java tutorial

Introduction

Here is the source code for de.jackwhite20.japs.client.sub.impl.SubscriberImpl.java

Source

/*
 * Copyright (c) 2016 "JackWhite20"
 *
 * This file is part of JaPS.
 *
 * JaPS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.jackwhite20.japs.client.sub.impl;

import com.google.gson.Gson;
import de.jackwhite20.japs.client.sub.Subscriber;
import de.jackwhite20.japs.client.sub.impl.handler.ChannelHandler;
import de.jackwhite20.japs.client.sub.impl.handler.ClassType;
import de.jackwhite20.japs.client.sub.impl.handler.HandlerInfo;
import de.jackwhite20.japs.client.sub.impl.handler.MultiHandlerInfo;
import de.jackwhite20.japs.client.sub.impl.handler.annotation.Channel;
import de.jackwhite20.japs.client.sub.impl.handler.annotation.Key;
import de.jackwhite20.japs.client.sub.impl.handler.annotation.Value;
import de.jackwhite20.japs.client.util.NameGeneratorUtil;
import de.jackwhite20.japs.shared.config.ClusterServer;
import de.jackwhite20.japs.shared.net.OpCode;
import de.jackwhite20.japs.shared.nio.NioSocketClient;
import org.json.JSONObject;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by JackWhite20 on 25.03.2016.
 */
public class SubscriberImpl extends NioSocketClient implements Subscriber {

    private static final AtomicInteger ID_COUNTER = new AtomicInteger(0);

    private Map<String, HandlerInfo> handlers = new HashMap<>();

    private Map<String, MultiHandlerInfo> multiHandlers = new HashMap<>();

    private Gson gson = new Gson();

    public SubscriberImpl(String host, int port) {

        this(host, port, NameGeneratorUtil.generateName("subscriber", ID_COUNTER.getAndIncrement()));
    }

    public SubscriberImpl(String host, int port, String name) {

        this(Collections.singletonList(new ClusterServer(host, port)), name);
    }

    public SubscriberImpl(List<ClusterServer> clusterServers) {

        this(clusterServers, NameGeneratorUtil.generateName("subscriber", ID_COUNTER.getAndIncrement()));
    }

    public SubscriberImpl(List<ClusterServer> clusterServers, String name) {

        super(clusterServers, name);
    }

    @Override
    public void clientConnected() {

        // Register with our name
        write(new JSONObject().put("op", OpCode.OP_SUBSCRIBER_SET_NAME.getCode()).put("su", name));
    }

    @Override
    public void clientReconnected() {

        if (handlers != null) {
            // Resubscribe the normal handlers
            for (Map.Entry<String, HandlerInfo> handlerInfoEntry : handlers.entrySet()) {
                subscribe(handlerInfoEntry.getValue().messageHandler().getClass());
            }
        }

        if (multiHandlers != null) {
            // Resubscribe the multi handlers
            for (Map.Entry<String, MultiHandlerInfo> handlerInfoEntry : multiHandlers.entrySet()) {
                subscribeMulti(handlerInfoEntry.getValue().object().getClass());
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void received(JSONObject jsonObject) {

        String channel = ((String) jsonObject.remove("ch"));

        if (channel == null || channel.isEmpty()) {
            return;
        }

        HandlerInfo handlerInfo = handlers.get(channel);

        if (handlerInfo != null) {
            if (handlerInfo.classType() == ClassType.JSON) {
                handlerInfo.messageHandler().onMessage(channel, jsonObject);
            } else {
                handlerInfo.messageHandler().onMessage(channel,
                        gson.fromJson(jsonObject.toString(), handlerInfo.clazz()));
            }
        } else {
            MultiHandlerInfo multiHandlerInfo = multiHandlers.get(channel);

            if (multiHandlerInfo != null) {
                //noinspection Convert2streamapi
                for (MultiHandlerInfo.Entry entry : multiHandlerInfo.entries()) {
                    if (!jsonObject.isNull(entry.key().value())) {
                        if (jsonObject.get(entry.key().value()).equals(entry.value().value())) {
                            // Remove matched key value pair
                            jsonObject.remove(entry.key().value());

                            if (entry.classType() == ClassType.JSON) {
                                try {
                                    // Invoke the matching method
                                    entry.method().invoke(multiHandlerInfo.object(), jsonObject);
                                } catch (IllegalAccessException | InvocationTargetException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                try {
                                    // Deserialize with gson
                                    entry.method().invoke(multiHandlerInfo.object(),
                                            gson.fromJson(jsonObject.toString(), entry.paramClass()));
                                } catch (IllegalAccessException | InvocationTargetException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private String getChannelFromAnnotation(Class<?> clazz) {

        if (!clazz.isAnnotationPresent(Channel.class)) {
            throw new IllegalArgumentException(
                    "the handler class " + clazz.getSimpleName() + " has no 'Channel' annotation");
        }

        String channel = clazz.getAnnotation(Channel.class).value();

        if (channel.isEmpty()) {
            throw new IllegalStateException(
                    "value of the 'Channel' annotation of class " + clazz.getSimpleName() + " is empty");
        }

        return channel;
    }

    @Override
    public void disconnect(boolean force) {

        close(force);
    }

    @Override
    public void disconnect() {

        disconnect(true);
    }

    @Override
    public boolean hasSubscription(String channel) {

        return handlers.containsKey(channel) || multiHandlers.containsKey(channel);
    }

    @Deprecated
    @Override
    public void subscribe(String channel, Class<? extends ChannelHandler> handler) {

        try {
            //noinspection unchecked
            handlers.put(channel, new HandlerInfo(handler.newInstance()));

            JSONObject jsonObject = new JSONObject().put("op", OpCode.OP_REGISTER_CHANNEL.getCode()).put("ch",
                    channel);

            write(jsonObject, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void subscribe(Class<? extends ChannelHandler> handler) {

        // Get channel and check the class for annotation etc.
        String channel = getChannelFromAnnotation(handler);

        //noinspection deprecation
        subscribe(channel, handler);
    }

    @Override
    public void subscribeMulti(Class<?> handler) {

        // Get channel and check the class for annotation etc.
        String channel = getChannelFromAnnotation(handler);

        try {
            List<MultiHandlerInfo.Entry> entries = new ArrayList<>();

            Object object = handler.newInstance();
            for (Method method : object.getClass().getDeclaredMethods()) {
                if (method.getParameterCount() == 1) {
                    if (method.isAnnotationPresent(Key.class) && method.isAnnotationPresent(Value.class)) {
                        entries.add(new MultiHandlerInfo.Entry(method.getAnnotation(Key.class),
                                method.getAnnotation(Value.class), method.getParameterTypes()[0],
                                (method.getParameterTypes()[0].getSimpleName().equals("JSONObject"))
                                        ? ClassType.JSON
                                        : ClassType.GSON,
                                method));
                    }
                }
            }

            multiHandlers.put(channel, new MultiHandlerInfo(entries, object));

            JSONObject jsonObject = new JSONObject().put("op", OpCode.OP_REGISTER_CHANNEL.getCode()).put("ch",
                    channel);

            write(jsonObject, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void unsubscribe(String channel) {

        // Only send unsubscribe if the channel is subscribed
        if (handlers.containsKey(channel) || multiHandlers.containsKey(channel)) {
            handlers.remove(channel);
            multiHandlers.remove(channel);

            JSONObject jsonObject = new JSONObject().put("op", OpCode.OP_UNREGISTER_CHANNEL.getCode()).put("ch",
                    channel);

            write(jsonObject);
        }
    }

    @Override
    public boolean connected() {

        return isConnected();
    }

    @Override
    public String name() {

        return name;
    }

    @Override
    public List<ClusterServer> clusterServers() {

        return Collections.unmodifiableList(super.clusterServers());
    }
}