org.eclipse.californium.extplugtests.ReceivetestClient.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.californium.extplugtests.ReceivetestClient.java

Source

/*******************************************************************************
 * Copyright (c) 2017 Bosch Software Innovations GmbH and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * 
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 *    http://www.eclipse.org/org/documents/edl-v10.html.
 * 
 * Contributors:
 *    Bosch Software Innovations GmbH - initial implementation
 *    Achim Kraus (Bosch Software Innovations GmbH) - use special properties file
 *                                                    for configuration
 ******************************************************************************/

package org.eclipse.californium.extplugtests;

import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
import static org.eclipse.californium.core.coap.MediaTypeRegistry.APPLICATION_JSON;
import static org.eclipse.californium.core.coap.MediaTypeRegistry.APPLICATION_CBOR;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.BindException;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Properties;
import java.util.UUID;

import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.Utils;
import org.eclipse.californium.core.coap.CoAP.ResponseCode;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.network.config.NetworkConfig.Keys;
import org.eclipse.californium.core.network.config.NetworkConfigDefaultHandler;
import org.eclipse.californium.elements.exception.ConnectorException;
import org.eclipse.californium.elements.util.StringUtil;
import org.eclipse.californium.plugtests.ClientInitializer;
import org.eclipse.californium.plugtests.ClientInitializer.Arguments;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.upokecenter.cbor.CBORException;
import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;

/**
 * The RecevietestClient uses the developer API of Californium to test the
 * communication. The server side keeps track of the last received request and
 * response with that history. So if a request fails, the nest request may show,
 * if the request was lost (not in history) or only the response was lost.
 */
public class ReceivetestClient {

    private static final File CONFIG_FILE = new File("CaliforniumReceivetest.properties");
    private static final String CONFIG_HEADER = "Californium CoAP Properties file for Receivetest Client";
    private static final int DEFAULT_MAX_RESOURCE_SIZE = 8192;
    private static final int DEFAULT_BLOCK_SIZE = 1024;

    /**
     * Properties filename for device UUID.
     */
    private static final String UUID_FILE = "UUID.properties";
    /**
     * Properties key for device UUID.
     */
    private static final String UUID_KEY = "UUID";
    /**
     * Prefix for request ID.
     */
    private static final String REQUEST_ID_PREFIX = "RID";

    /**
     * Maximum time difference display as milliseconds.
     */
    private static final int MAX_DIFF_TIME_IN_MILLIS = 30000;

    private static NetworkConfigDefaultHandler DEFAULTS = new NetworkConfigDefaultHandler() {

        @Override
        public void applyDefaults(NetworkConfig config) {
            config.setInt(Keys.MAX_RESOURCE_BODY_SIZE, DEFAULT_MAX_RESOURCE_SIZE);
            config.setInt(Keys.MAX_MESSAGE_SIZE, DEFAULT_BLOCK_SIZE);
            config.setInt(Keys.PREFERRED_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);
            config.setInt(Keys.MAX_ACTIVE_PEERS, 10);
        }
    };

    /**
     * Main entry point.
     * 
     * @param args the arguments
     */
    public static void main(String[] args) throws ConnectorException, IOException {

        if (args.length == 0) {

            System.out.println("\nCalifornium (Cf) Receivetest Client");
            System.out.println("(c) 2017, Bosch Software Innovations GmbH and others");
            System.out.println();
            System.out.println(
                    "Usage: " + ReceivetestClient.class.getSimpleName() + " [-v] [-j|-c] [-r|-x|-i id pw] URI");
            System.out.println("  -v        : verbose. Enable message tracing.");
            System.out.println("  -j        : use JSON format.");
            System.out.println("  -c        : use CBOR format.");
            System.out.println("  -r        : use raw public certificate. Default PSK.");
            System.out.println("  -x        : use x.509 certificate");
            System.out.println("  -i id pw  : use PSK with id and password");
            System.out.println(
                    "  URI       : The CoAP URI of the extended Plugtest server to test (coap://<host>[:<port>])");
            System.out.println();
            System.out.println(
                    "Example: " + ReceivetestClient.class.getSimpleName() + " coap://californium.eclipse.org:5783");
            System.exit(-1);
        }

        NetworkConfig config = NetworkConfig.createWithFile(CONFIG_FILE, CONFIG_HEADER, DEFAULTS);

        Arguments arguments;
        try {
            arguments = ClientInitializer.init(config, args, false);
        } catch (BindException ex) {
            arguments = ClientInitializer.init(config, args, true);
            System.out.println("Default port not available, use ephemeral port!");
        }

        String uuid = getUUID();
        CoapClient client = new CoapClient(arguments.uri);
        Request request = Request.newPost();
        if (arguments.json) {
            request.getOptions().setAccept(APPLICATION_JSON);
        } else if (arguments.cbor) {
            request.getOptions().setAccept(APPLICATION_CBOR);
        }
        request.setURI(arguments.uri + "/requests?dev=" + uuid + "&rid=" + REQUEST_ID_PREFIX
                + System.currentTimeMillis() + "&ep");
        CoapResponse coapResponse = client.advanced(request);

        if (coapResponse != null) {
            ResponseCode code = coapResponse.getCode();
            int format = coapResponse.getOptions().getContentFormat();
            if (CONTENT == code && format == APPLICATION_JSON) {
                // JSON success
                System.out.println();
                Response response = coapResponse.advanced();
                String payload = response.getPayloadString();
                System.out.println("Payload: " + payload.length() + " bytes");
                Long rtt = response.getRTT();
                if (rtt != null) {
                    System.out.println("RTT: " + rtt + "ms");
                }
                System.out.println();
                String statistic = processJSON(response.getPayloadString(), "", arguments.verbose);
                System.out.println(statistic);
            } else if (CONTENT == code && format == APPLICATION_CBOR) {
                // CBOR success
                System.out.println();
                Response response = coapResponse.advanced();
                String payload = response.getPayloadString();
                System.out.println("Payload: " + payload.length() + " bytes");
                Long rtt = response.getRTT();
                if (rtt != null) {
                    System.out.println("RTT: " + rtt + "ms");
                }
                System.out.println();
                String statistic = processCBOR(response.getPayload(), "", arguments.verbose);
                System.out.println(statistic);
            } else {
                System.out.println(coapResponse.getCode());
                System.out.println(coapResponse.getOptions());
                System.out.println(System.lineSeparator() + "ADVANCED" + System.lineSeparator());
                System.out.println(Utils.prettyPrint(coapResponse));
            }
        } else {
            System.out.println("No response received.");
        }
        client.shutdown();
        System.exit(0);
    }

    /**
     * Process JSON response.
     * 
     * @param payload JSON payload as string
     * @param errors request ID of previously failed request.
     * @param verbose {@code true} to interpret the string, either pretty JSON,
     *            or pretty application, if data complies the assumed request
     *            statistic information.
     * @return payload as application text, pretty JSON, or just as provided
     *         text.
     */
    public static String processJSON(String payload, String errors, boolean verbose) {
        StringBuilder statistic = new StringBuilder();
        JsonElement element = null;
        try {
            JsonParser parser = new JsonParser();
            element = parser.parse(payload);

            if (verbose && element.isJsonArray()) {
                // expected JSON data
                SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy");
                try {
                    for (JsonElement item : element.getAsJsonArray()) {
                        if (!item.isJsonObject()) {
                            // unexpected =>
                            // stop application pretty printing
                            statistic.setLength(0);
                            break;
                        }
                        JsonObject object = item.getAsJsonObject();
                        if (object.has("rid")) {
                            String rid = object.get("rid").getAsString();
                            long time = object.get("time").getAsLong();
                            if (rid.startsWith(REQUEST_ID_PREFIX)) {
                                boolean hit = errors.contains(rid);
                                rid = rid.substring(REQUEST_ID_PREFIX.length());
                                long requestTime = Long.parseLong(rid);
                                statistic.append("Request: ").append(format.format(requestTime));
                                long diff = time - requestTime;
                                if (-MAX_DIFF_TIME_IN_MILLIS < diff && diff < MAX_DIFF_TIME_IN_MILLIS) {
                                    statistic.append(", received: ").append(diff).append(" ms");
                                } else {
                                    statistic.append(", received: ").append(format.format(time));
                                }
                                if (hit) {
                                    statistic.append(" * lost response!");
                                }
                            } else {
                                statistic.append("Request: ").append(rid);
                                statistic.append(", received: ").append(format.format(time));
                            }
                            if (object.has("ep")) {
                                String endpoint = object.get("ep").getAsString();
                                if (endpoint.contains(":")) {
                                    endpoint = "[" + endpoint + "]";
                                }
                                int port = object.get("port").getAsInt();
                                statistic.append(System.lineSeparator());
                                statistic.append("    (").append(endpoint).append(":").append(port).append(")");
                            }
                            statistic.append(System.lineSeparator());
                        } else {
                            long time = object.get("systemstart").getAsLong();
                            statistic.append("Server's system start: ").append(format.format(time));
                            statistic.append(System.lineSeparator());
                        }
                    }
                } catch (Throwable e) {
                    // unexpected => stop application pretty printing
                    statistic.setLength(0);
                }
            }
            if (statistic.length() == 0) {
                // JSON plain pretty printing
                GsonBuilder builder = new GsonBuilder();
                builder.setPrettyPrinting();
                Gson gson = builder.create();
                gson.toJson(element, statistic);
            }
        } catch (JsonSyntaxException e) {
            // plain payload
            e.printStackTrace();
            statistic.setLength(0);
            statistic.append(payload);
        }
        return statistic.toString();
    }

    public static String processCBOR(byte[] payload, String errors, boolean verbose) {
        try {
            StringBuilder statistic = new StringBuilder();
            CBORObject element = CBORObject.DecodeFromBytes(payload);
            if (verbose && element.getType() == CBORType.Array) {
                // expected JSON data
                SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy");
                try {
                    for (CBORObject item : element.getValues()) {
                        if (item.getType() != CBORType.Map) {
                            // unexpected =>
                            // stop application pretty printing
                            statistic.setLength(0);
                            break;
                        }
                        CBORObject value;
                        if ((value = item.get("rid")) != null) {
                            String rid = value.AsString();
                            long time = item.get("time").AsInt64();
                            if (rid.startsWith(REQUEST_ID_PREFIX)) {
                                boolean hit = errors.contains(rid);
                                rid = rid.substring(REQUEST_ID_PREFIX.length());
                                long requestTime = Long.parseLong(rid);
                                statistic.append("Request: ").append(format.format(requestTime));
                                long diff = time - requestTime;
                                if (-MAX_DIFF_TIME_IN_MILLIS < diff && diff < MAX_DIFF_TIME_IN_MILLIS) {
                                    statistic.append(", received: ").append(diff).append(" ms");
                                } else {
                                    statistic.append(", received: ").append(format.format(time));
                                }
                                if (hit) {
                                    statistic.append(" * lost response!");
                                }
                            } else {
                                statistic.append("Request: ").append(rid);
                                statistic.append(", received: ").append(format.format(time));
                            }
                            if ((value = item.get("ep")) != null) {
                                byte[] endpoint = value.GetByteString();
                                int port = item.get("port").AsInt16() & 0xffff;
                                statistic.append(System.lineSeparator());
                                String address = InetAddress.getByAddress(endpoint).getHostAddress();
                                if (address.contains(":")) {
                                    address = "[" + address + "]";
                                }
                                statistic.append("    (").append(address).append(":").append(port).append(")");
                            }
                            statistic.append(System.lineSeparator());
                        } else {
                            long time = item.get("systemstart").AsInt64();
                            statistic.append("Server's system start: ").append(format.format(time));
                            statistic.append(System.lineSeparator());
                        }
                    }
                } catch (Throwable e) {
                    // unexpected => stop application pretty printing
                    statistic.setLength(0);
                }
            }
            if (statistic.length() > 0) {
                return statistic.toString();
            } else {
                // CBOR plain pretty printing
                return element.toString();
            }
        } catch (CBORException e) {
            // plain payload
            e.printStackTrace();
            return StringUtil.byteArray2Hex(payload);
        }
    }

    /**
     * Get UUID as device ID.
     * 
     * Read UUID from {@link #UUID_FILE} properties file with key
     * {@link #UUID_KEY}, if file is available. If the file has no
     * {@link #UUID_KEY}, use "anonymous" as UUID. If the properties file is not
     * available, create a new random UUID and store it in that file.
     * 
     * @return UUID
     */
    public static String getUUID() {
        Properties props = new Properties();
        try (FileReader reader = new FileReader(UUID_FILE)) {
            props.load(reader);
            String uid = props.getProperty(UUID_KEY);
            if (uid == null) {
                uid = "anonymous";
            }
            return uid;
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        }
        try (FileWriter writer = new FileWriter(UUID_FILE)) {
            String uid = UUID.randomUUID().toString();
            props.setProperty(UUID_KEY, uid);
            props.store(writer, "Californium CoAP UUID file");
            return uid;
        } catch (IOException e) {
        }
        return "anonymous";
    }
}