com.hbm.devices.scan.announce.AnnounceDeserializer.java Source code

Java tutorial

Introduction

Here is the source code for com.hbm.devices.scan.announce.AnnounceDeserializer.java

Source

/*
 * Java Scan, a library for scanning and configuring HBM devices.
 *
 * The MIT License (MIT)
 *
 * Copyright (C) Hottinger Baldwin Messtechnik GmbH
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.hbm.devices.scan.announce;

import java.lang.reflect.Type;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;

import com.hbm.devices.scan.JsonRpc;
import com.hbm.devices.scan.ScanConstants;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * This class gets JSON announce messages, parses them and notifies
 * {@link Announce} objects.
 * <p>
 * The whole class is designed as a best effort service. So invalid JSON
 * messages, or messages that do not conform to the HBM network discovery and
 * configuration protocol are simply ignored. Users of this class will
 * <em>not</em> get any error messages or exceptions.
 *
 * @since 1.0
 */
public final class AnnounceDeserializer extends Observable implements Observer {

    private final Gson gson;
    private final AnnounceCache announceCache;
    private static final Logger LOGGER = Logger.getLogger(ScanConstants.LOGGER_NAME);

    /**
     * Constructs a {@link AnnounceDeserializer} object.
     */
    public AnnounceDeserializer() {
        super();

        final GsonBuilder builder = new GsonBuilder();
        builder.registerTypeAdapter(JsonRpc.class, new JsonRpcDeserializer());
        builder.registerTypeAdapter(AnnounceParams.class, new AnnounceParamsDeserializer());
        Type serviceListType = new TypeToken<List<ServiceEntry>>() {
        }.getType();
        builder.registerTypeAdapter(serviceListType, new ServiceDeserializer());
        builder.registerTypeAdapter(Interface.class, new InterfaceDeserializer());

        //  builder.registerTypeAdapter(IPEntry.class, new IPv4Deserializer());
        gson = builder.create();

        this.announceCache = new AnnounceCache();
    }

    AnnounceCache getCache() {
        return this.announceCache;
    }

    @Override
    public void update(Observable observable, Object arg) {
        final String message = (String) arg;
        Announce announce = announceCache.get(message);
        if (announce == null) {
            try {
                announce = (Announce) gson.fromJson(message, JsonRpc.class);
                if (announce != null) {
                    announce.identifyCommunicationPath();
                    if (announce.getParams().getExpiration() < 0) {
                        return;
                    }
                    announceCache.put(message, announce);
                    setChanged();
                    notifyObservers(announce);
                }
            } catch (JsonSyntaxException e) {
                /*
                 * There is no error handling necessary in this case. If somebody sends us invalid JSON,
                 * we just ignore the packet and go ahead.
                 */
                LOGGER.log(Level.SEVERE, "Can't parse JSON!", e);
            } catch (MissingDataException e) {
                /*
                 * During the creation of an Announce object it is required that some
                 * sub-objects are created in the parsed JSON object (i.e. the device's UUID). If these
                 * sub-objects are not created, the construction of the Announce object fails.
                 * 
                 * Go ahead with the next packet.
                 */
                LOGGER.log(Level.SEVERE, "Some information is missing in JSON!", e);
            }
        } else {
            setChanged();
            notifyObservers(announce);
        }
    }

    private static final class JsonRpcDeserializer implements JsonDeserializer<JsonRpc> {

        JsonRpcDeserializer() {
            // This constructor is only use by the outer class.
        }

        @Override
        public JsonRpc deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {

            JsonRpc rpcObject = null;
            final JsonObject jsonObject = json.getAsJsonObject();

            if (jsonObject.has("method")) {
                final String type = jsonObject.get("method").getAsString();
                if ("announce".compareTo(type) == 0) {
                    rpcObject = context.deserialize(json, Announce.class);
                    rpcObject.setJSONString(jsonObject.toString());
                }
            }
            return rpcObject;
        }
    }

    private static final class InterfaceDeserializer implements JsonDeserializer<Interface> {

        @Override
        public Interface deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {
            final JsonObject jsonObject = json.getAsJsonObject();

            JsonElement name = jsonObject.get("name");
            if (name != null) {
                String interfaceName = name.getAsString();
                if (interfaceName != null) {
                    Interface iface = new Interface();
                    iface.name = interfaceName;
                    JsonElement description = jsonObject.get("description");
                    if (description != null) {
                        iface.description = description.getAsString();
                    }

                    JsonElement type = jsonObject.get("type");
                    if (type != null) {
                        iface.type = type.getAsString();
                    }

                    iface.ipList = new LinkedList<>();
                    JsonElement ipv4 = jsonObject.get("ipv4");
                    if (ipv4 != null) {
                        for (JsonElement e : ipv4.getAsJsonArray()) {
                            JsonElement address = e.getAsJsonObject().get("address");
                            JsonElement netMask = e.getAsJsonObject().get("netmask");
                            if ((address != null) && (netMask != null)) {
                                InetAddress value = context.deserialize(address, InetAddress.class);
                                InetAddress mask = context.deserialize(netMask, InetAddress.class);
                                if ((value != null) && (mask != null)) {
                                    IPEntry entry = new IPEntry();
                                    entry.address = value;
                                    entry.prefix = calculatePrefix(mask);
                                    iface.ipList.add(entry);
                                }
                            }
                        }
                    }

                    JsonElement ipv6 = jsonObject.get("ipv6");
                    if (ipv6 != null) {
                        for (JsonElement e : ipv6.getAsJsonArray()) {
                            JsonElement address = e.getAsJsonObject().get("address");
                            JsonElement prefix = e.getAsJsonObject().get("prefix");
                            if ((address != null) && (prefix != null)) {
                                InetAddress value = context.deserialize(address, InetAddress.class);
                                if (value != null) {
                                    IPEntry entry = new IPEntry();
                                    entry.address = value;
                                    entry.prefix = prefix.getAsInt();
                                    iface.ipList.add(entry);
                                }
                            }
                        }
                    }

                    return iface;
                }
            }

            return null;
        }
    }

    private static final class ServiceDeserializer implements JsonDeserializer<List<ServiceEntry>> {

        @Override
        public List<ServiceEntry> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {
            List<ServiceEntry> vals = null;
            if (json.isJsonArray()) {
                vals = new ArrayList<>();
                for (JsonElement e : json.getAsJsonArray()) {
                    vals.add((ServiceEntry) context.deserialize(e, ServiceEntry.class));
                }
            }

            return vals;
        }
    }

    private static final class AnnounceParamsDeserializer implements JsonDeserializer<AnnounceParams> {

        private final Type serviceListType;

        AnnounceParamsDeserializer() {
            serviceListType = new TypeToken<List<ServiceEntry>>() {
            }.getType();
        }

        @Override
        public AnnounceParams deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
            AnnounceParams params = null;
            final JsonObject jsonObject = json.getAsJsonObject();
            if (jsonObject.has("apiVersion")) {
                final String version = jsonObject.get("apiVersion").getAsString();
                if ("1.0".compareTo(version) == 0) {
                    params = new AnnounceParams();
                    params.apiVersion = version;

                    Device device = context.deserialize(jsonObject.get("device"), Device.class);
                    params.device = device;
                    NetSettings netSettings = context.deserialize(jsonObject.get("netSettings"), NetSettings.class);
                    params.netSettings = netSettings;
                    Router router = context.deserialize(jsonObject.get("router"), Router.class);
                    params.router = router;
                    JsonElement e = jsonObject.get("services");
                    List<ServiceEntry> services = context.deserialize(e, serviceListType);
                    params.services = services;
                    e = jsonObject.get("expiration");
                    if (e != null) {
                        params.expiration = e.getAsInt();
                    }
                } else if (LOGGER.isLoggable(Level.INFO)) {
                    LOGGER.log(Level.INFO, "Can't handle apiVersion: {0}\n{1}",
                            new Object[] { version, jsonObject });
                }
            } else if (LOGGER.isLoggable(Level.SEVERE)) {
                LOGGER.log(Level.SEVERE, "No apiVersion set in announce packet!\n{0}", jsonObject);
            }
            return params;
        }
    }

    static int calculatePrefix(InetAddress announceNetmask) {
        final byte[] address = announceNetmask.getAddress();
        final int length = address.length;
        int prefix = 0;
        for (int i = 0; i < length; i++) {
            prefix += Integer.bitCount(address[i] & 0xff);
        }
        return prefix;
    }
}