vitro.vgw.wsiadapter.WSIAdapterCoap.java Source code

Java tutorial

Introduction

Here is the source code for vitro.vgw.wsiadapter.WSIAdapterCoap.java

Source

/*
 * #--------------------------------------------------------------------------
 * # Copyright (c) 2013 VITRO FP7 Consortium.
 * # All rights reserved. This program and the accompanying materials
 * # are made available under the terms of the GNU Lesser Public License v3.0 which accompanies this distribution, and is available at
 * # http://www.gnu.org/licenses/lgpl-3.0.html
 * #
 * # Contributors:
 * #     Antoniou Thanasis (Research Academic Computer Technology Institute)
 * #     Paolo Medagliani (Thales Communications & Security)
 * #     D. Davide Lamanna (WLAB SRL)
 * #     Alessandro Leoni (WLAB SRL)
 * #     Francesco Ficarola (WLAB SRL)
 * #     Stefano Puglia (WLAB SRL)
 * #     Panos Trakadas (Technological Educational Institute of Chalkida)
 * #     Panagiotis Karkazis (Technological Educational Institute of Chalkida)
 * #     Andrea Kropp (Selex ES)
 * #     Kiriakos Georgouleas (Hellenic Aerospace Industry)
 * #     David Ferrer Figueroa (Telefonica Investigacin y Desarrollo S.A.)
 * #
 * #--------------------------------------------------------------------------
 */
package vitro.vgw.wsiadapter;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import alter.vitro.vgw.wsiadapter.InfoOnTrustRouting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.ws4d.coap.connection.BasicCoapChannelManager;
import org.ws4d.coap.interfaces.CoapChannelManager;
import org.ws4d.coap.interfaces.CoapClient;
import org.ws4d.coap.interfaces.CoapClientChannel;
import org.ws4d.coap.interfaces.CoapRequest;
import org.ws4d.coap.interfaces.CoapResponse;
import org.ws4d.coap.messages.CoapEmptyMessage;
import org.ws4d.coap.messages.CoapRequestCode;

import vitro.vgw.exception.VitroGatewayException;
import vitro.vgw.exception.WSIAdapterException;
import vitro.vgw.model.Node;
import vitro.vgw.model.Observation;
import vitro.vgw.model.Resource;
import vitro.vgw.wsiadapter.coap.model.MoteResource;
import vitro.vgw.wsiadapter.coap.model.Network;
import vitro.vgw.wsiadapter.coap.util.Constants;
import vitro.vgw.wsiadapter.coap.util.Functions;
import org.apache.commons.codec.binary.Base64;

public class WSIAdapterCoap implements WSIAdapter, CoapClient, Observer {
    private static final int UNDEFINED_COAP_MESSAGE_ID = -1;
    static ArrayList<Integer> timedOutCoapMessageIDsList = new ArrayList<Integer>();
    static ArrayList<Integer> timedOut_DTN_CoapMessageIDsList = new ArrayList<Integer>(); //stores packet Ids
    //15/04
    static HashMap<String, CoapClientChannel> proxyAddrToClientChannelResourcesHM = new HashMap<String, CoapClientChannel>();
    static HashMap<String, CoapClientChannel> proxyAddrToClientChannelObservationsHM = new HashMap<String, CoapClientChannel>();

    /**
     * @author Francesco Ficarola (ficarola<at>dis.uniroma1<dot>it)
     */

    private Logger logger = LoggerFactory.getLogger(WSIAdapterCoap.class);
    private CountDownLatch signal;

    private final int COAP_PORT = Constants.COAP_DEFAULT_PORT;

    private CoapChannelManager channelManager;
    //private CoapClientChannel clientChannelObservations;
    //private CoapClientChannel clientChannelResources;

    private List<Resource> resourceList;
    private String resourceValue;

    private DTNResponder dtnServer;
    private Thread threadDTN;
    private Random random;
    private boolean isDtnEnabled;
    private boolean trustCoapMessagingActivated;
    private final String RESOURCE_REQ = "1";

    private String exceptionError;

    private List<Node> nodesList;

    public WSIAdapterCoap() {
        signal = null;
        channelManager = null;
        //clientChannelObservations = null;
        //clientChannelResources = null;
        resourceList = new ArrayList<Resource>();
        resourceValue = null;
        exceptionError = "";
        random = new Random();
        dtnServer = new DTNResponder(Constants.DTN_VGW_PORT, this);
        threadDTN = new Thread(dtnServer);
        isDtnEnabled = false;
        trustCoapMessagingActivated = false;
        channelManager = BasicCoapChannelManager.getInstance();

        nodesList = new LinkedList<Node>();
    }

    /**
     * WSIAdapter interface
     */

    public List<Node> getAvailableNodeList() throws WSIAdapterException {

        logger.info("Getting available nodes...");

        nodesList = new ArrayList<Node>();

        List<String> wsnProxyList = new LinkedList<String>();
        wsnProxyList.add(Network.WLAB_OFFICE_PROXY_ADDRESS);
        wsnProxyList.add(Network.WLAB_LAB_PROXY_ADDRESS);

        DatagramSocket serverSocket = null;
        DatagramSocket clientSocket = null;
        String cmdString = "route";

        for (int i = 0; i < wsnProxyList.size(); i++) {
            try {
                serverSocket = new DatagramSocket(Constants.UDPSHELL_VGW_PORT);

                String hostProxyString = wsnProxyList.get(i);
                InetAddress hostProxy = InetAddress.getByName(hostProxyString);

                clientSocket = new DatagramSocket();
                byte[] bufCmd = new byte[10];
                bufCmd = cmdString.getBytes();
                DatagramPacket outcomingPacket = new DatagramPacket(bufCmd, bufCmd.length, hostProxy,
                        Constants.PROXY_UDPFORWARDER_PORT);
                clientSocket.send(outcomingPacket);

                boolean otherPackets = false;

                serverSocket.setSoTimeout(Constants.PROXY_RESPONSE_TIMEOUT);
                logger.info("Quering " + hostProxyString);
                try {
                    byte[] bufAck = new byte[10];
                    DatagramPacket ackPacket = new DatagramPacket(bufAck, bufAck.length);
                    serverSocket.receive(ackPacket);
                    String ackString = new String(ackPacket.getData()).trim();
                    if (ackString.equals("ack")) {
                        otherPackets = true;
                    }
                } catch (SocketTimeoutException e) {
                    logger.warn(e.getMessage());
                }

                serverSocket.setSoTimeout(0);

                while (otherPackets) {
                    try {
                        byte[] bufIncoming = new byte[1000];
                        DatagramPacket incomingPacket = new DatagramPacket(bufIncoming, bufIncoming.length);
                        serverSocket.receive(incomingPacket);
                        String currentNodeIP = new String(incomingPacket.getData()).trim();
                        if (!currentNodeIP.equals("end")) {
                            logger.info("Node: " + currentNodeIP);
                            nodesList.add(new Node(currentNodeIP));
                        } else {
                            otherPackets = false;
                            logger.info("No other nodes from " + hostProxyString);
                        }
                    } catch (IOException e) {
                        logger.error(e.getMessage());
                    }
                }

            } catch (UnknownHostException e) {
                logger.warn(e.getMessage() + " is not reachable.");
            } catch (SocketException e) {
                logger.error(e.getMessage());
            } catch (IOException e) {
                logger.error(e.getMessage());
            } finally {
                if (serverSocket != null) {
                    serverSocket.close();
                }
                if (clientSocket != null) {
                    clientSocket.close();
                }
            }
        }

        return nodesList;
    }

    public List<Resource> getResources(Node node) throws WSIAdapterException {

        signal = new CountDownLatch(1);
        List<Resource> resourceList = new ArrayList<Resource>();
        int requestMessageID = UNDEFINED_COAP_MESSAGE_ID;
        try {
            boolean requestWasRespondedWithinTimelimit = false;
            if (isDtnEnabled) {
                requestMessageID = dtnResourcesRequest(node);
                requestWasRespondedWithinTimelimit = signal.await(Constants.DTN_REQUEST_TIMEOUT, TimeUnit.MINUTES);
            } else {
                requestMessageID = coapResourcesRequest(node);
                // TODO: the signal await function will return FALSE if the timeout has expired.
                // This allows to manage the timeout case, and possibly the re-sending of messages that the coap gateway will do (until receiving a response)
                // The plan is that if this request times out, the ID of the request is logged in a static/global table and we further ignore messages for this ID (we don't handle them anymore).
                requestWasRespondedWithinTimelimit = signal.await(Constants.SIMPLE_COAP_REQUEST_TIMEOUT_SECONDS,
                        TimeUnit.SECONDS);
            }
            if (!requestWasRespondedWithinTimelimit) { //if we had timeout
                if (isDtnEnabled) //for DTN mode, iwe add it to a separate list (different callback function and messages )
                {
                    // TODO: we need to make something similar for DTN too!
                    if (requestMessageID != UNDEFINED_COAP_MESSAGE_ID
                            && !timedOut_DTN_CoapMessageIDsList.contains(Integer.valueOf(requestMessageID))) {
                        timedOut_DTN_CoapMessageIDsList.add(Integer.valueOf(requestMessageID));
                    }

                } else {
                    //add the messageId to the list of IDs to ignore in the future (the check will be done in the callback function
                    if (requestMessageID != UNDEFINED_COAP_MESSAGE_ID
                            && !timedOutCoapMessageIDsList.contains(Integer.valueOf(requestMessageID))) {
                        timedOutCoapMessageIDsList.add(Integer.valueOf(requestMessageID));
                    }
                }
            } else {
                if (this.resourceList.size() > 0) {

                    resourceList.addAll(this.resourceList);
                    this.resourceList.clear();

                } else {

                    String error = "";
                    if (!exceptionError.equals("")) {
                        error = exceptionError;
                        exceptionError = "";
                    } else {
                        error = "No available resources for Node " + node.getId();
                    }

                    throw new WSIAdapterException(error);
                }
            }

        } catch (InterruptedException e) {
            throw new WSIAdapterException(e.getMessage(), e);
        } catch (UnknownHostException e) {
            throw new WSIAdapterException(e.getMessage(), e);
        } catch (IOException e) {
            throw new WSIAdapterException(e.getMessage(), e);
        }

        return resourceList;
    }

    public Observation getNodeObservation(Node node, Resource resource) throws WSIAdapterException {

        signal = new CountDownLatch(1);
        Observation observation = new Observation();

        try {
            int requestMessageID = UNDEFINED_COAP_MESSAGE_ID;
            boolean requestWasRespondedWithinTimelimit = false;
            if (isDtnEnabled) {
                requestMessageID = dtnObservationRequest(node, resource);
                requestWasRespondedWithinTimelimit = signal.await(Constants.DTN_REQUEST_TIMEOUT, TimeUnit.MINUTES);
            } else {
                requestMessageID = coapObservationRequest(node, resource);
                // TODO: the signal await function will return FALSE if the timeout has expired.
                // This allows to manage the timeout case, and possibly the re-sending of messages that the coap gateway will do (until receiving a response)
                // The plan is that if this request times out, the ID of the request is logged in a static/global table and we further ignore messages for this ID (we don't handle them anymore).
                requestWasRespondedWithinTimelimit = signal.await(Constants.SIMPLE_COAP_REQUEST_TIMEOUT_SECONDS,
                        TimeUnit.SECONDS);
            }
            if (!requestWasRespondedWithinTimelimit) { //if we had timeout
                if (isDtnEnabled) //for DTN mode, we add it to a separate list (different callback function and messages )
                {
                    if (requestMessageID != UNDEFINED_COAP_MESSAGE_ID
                            && !timedOut_DTN_CoapMessageIDsList.contains(Integer.valueOf(requestMessageID))) {
                        timedOut_DTN_CoapMessageIDsList.add(Integer.valueOf(requestMessageID));
                    }
                } else {
                    //add the messageId to the list of IDs to ignore in the future (the check will be done in the callback function
                    if (requestMessageID != UNDEFINED_COAP_MESSAGE_ID
                            && !timedOutCoapMessageIDsList.contains(Integer.valueOf(requestMessageID))) {
                        timedOutCoapMessageIDsList.add(Integer.valueOf(requestMessageID));
                    }
                }
            } else {
                if (resourceValue != null) {

                    observation.setNode(node);
                    observation.setResource(resource);
                    observation.setValue(formatResourceValue(resourceValue, resource));
                    logger.debug("Retrieved and stored observation value: " + observation.getValue());
                    observation.setTimestamp(System.currentTimeMillis());
                    resourceValue = null;

                } else {

                    String error = "";
                    if (!exceptionError.equals("")) {
                        error = exceptionError;
                        exceptionError = "";
                    } else {
                        error = "No available resources for Node " + node.getId() + " and Resource "
                                + resource.getName();
                    }

                    throw new WSIAdapterException(error);
                }
            }

        } catch (UnknownHostException e) {
            throw new WSIAdapterException(e.getMessage(), e);
        } catch (InterruptedException e) {
            throw new WSIAdapterException(e.getMessage(), e);
        } catch (VitroGatewayException e) {
            throw new WSIAdapterException(e.getMessage(), e);
        } catch (IOException e) {
            throw new WSIAdapterException(e.getMessage(), e);
        }

        return observation;
    }

    /**
     * DTN methods
     */

    public void update(Observable obs, Object obj) {
        String dtnMsg = (String) obj;
        try {
            onDtnResponse(dtnMsg);
        } catch (VitroGatewayException e) {
            logger.error(e.getMessage());
        }
    }

    private void onDtnResponse(String dtnMsg) throws VitroGatewayException {
        boolean countDownTheSignal = false;

        List<String> msgElements = new LinkedList<String>();
        StringTokenizer st = new StringTokenizer(dtnMsg, "#");
        while (st.hasMoreTokens()) {
            msgElements.add(st.nextToken().trim());
        }

        if (msgElements.size() != 4) {
            logger.error("Malformed DTN message: " + dtnMsg);
            //return;  // and no countdown is performed
        } else {
            String packetID = msgElements.get(0);
            String serverID = msgElements.get(1);
            String packetType = msgElements.get(2);
            String packetBody = msgElements.get(3);

            if (packetID != null && !packetID.trim().isEmpty()) {
                Integer packetIDInt = Integer.valueOf(UNDEFINED_COAP_MESSAGE_ID);
                try {
                    packetIDInt = Integer.valueOf(packetID);
                } catch (NumberFormatException nfe1) {
                    logger.debug("NUMBER FORMAT EXCEPTION FOR DTN Packet ID: " + packetID);
                }
                if (packetIDInt == Integer.valueOf(UNDEFINED_COAP_MESSAGE_ID)
                        || timedOut_DTN_CoapMessageIDsList.contains(Integer.valueOf(packetID))) {
                    countDownTheSignal = false;
                    logger.debug("IGNORING TIMED OUT DTN Packet ID: " + packetID);
                    // TODO: if it is contained, do we remove it to clean up the list? (also the messageID for the coapMessages could be rotating, so we would need clean-up !!)
                    //                          or do we expect further responses for a timeout message (due to the re-transmissions?)
                } else {
                    logger.info("DTN Packet ID: " + packetID);
                    logger.info("DTN Server ID: " + serverID);
                    logger.info("DTN Packet Type: " + packetType);
                    logger.info("DTN Packet Body: " + packetBody);

                    List<String> bodyElements = new LinkedList<String>();
                    st = new StringTokenizer(packetBody, "*");
                    while (st.hasMoreTokens()) {
                        bodyElements.add(st.nextToken().trim());
                    }

                    if (bodyElements.get(0).equals("wkc")) {
                        for (int i = 1; i < bodyElements.size(); i++) {
                            String resourceName = bodyElements.get(i);
                            if (MoteResource.containsKey(resourceName)) {
                                logger.debug("Resource name: " + resourceName);
                                Resource resource = MoteResource.getResource(resourceName);
                                resourceList.add(resource);
                            }
                        }
                    } else if (bodyElements.get(0).equals("st")) {
                        Integer readingValue = Integer.parseInt(bodyElements.get(1));
                        int rawValue = 23355 + readingValue - 200;
                        resourceValue = String.valueOf(rawValue);
                    } else if (bodyElements.get(0).equals("sh")) {
                        Integer readingValue = Integer.parseInt(bodyElements.get(1));
                        int rawvalue = -204 + readingValue * 4 - (readingValue * readingValue) * 100 / 628931;
                        resourceValue = String.valueOf(rawvalue);
                    }
                    countDownTheSignal = true;
                }
            } else {
                logger.debug("EMPTY OR NULL DTN Packet ID found!");
            }
        }
        if (countDownTheSignal) {
            signal.countDown();
        }
    }

    public boolean getDtnPolicy() {
        return isDtnEnabled;
    }

    public void setDtnPolicy(boolean value) {
        isDtnEnabled = value;

        if (isDtnEnabled) {
            if (!threadDTN.isAlive()) {
                threadDTN.start();
            }
        }
    }

    private int dtnResourcesRequest(Node node) throws WSIAdapterException, IOException {
        int requestMsgId = UNDEFINED_COAP_MESSAGE_ID; // TODO: ??? we will use the packetID as a message ID to return.
        String proxyAddress = getProxyAddress(node);
        if (proxyAddress != null) {
            int packetID = UNDEFINED_COAP_MESSAGE_ID;
            do { //while loop to avoid a packetID that exists in the array that is to be ignored!
                packetID = random.nextInt(65535) + 1;
            } while (timedOut_DTN_CoapMessageIDsList.contains(Integer.valueOf(packetID))
                    || packetID == UNDEFINED_COAP_MESSAGE_ID);

            String msgString = Integer.toString(packetID) + "#" + node.getId() + "#" + RESOURCE_REQ + "#wkc";
            byte[] msgBytes = new byte[Constants.DTN_MESSAGE_SIZE];
            msgBytes = msgString.getBytes();
            DatagramPacket sendPacket = new DatagramPacket(msgBytes, msgBytes.length,
                    InetAddress.getByName(proxyAddress), Constants.PROXY_UDPFORWARDER_PORT);
            DatagramSocket clientSocket = new DatagramSocket();
            clientSocket.send(sendPacket);
            clientSocket.close();
            requestMsgId = packetID;
            logger.info("Sent Request: " + msgString);
        } else {
            logger.warn("No available proxy for Node " + node.getId() + " is found");
            throw new WSIAdapterException("No available proxy for Node " + node.getId() + " is found");
        }
        return requestMsgId;
    }

    private int dtnObservationRequest(Node node, Resource resource) throws VitroGatewayException, IOException {
        int requestMsgId = UNDEFINED_COAP_MESSAGE_ID; // TODO: ??? we will use the packetID as a message ID to return.
        String proxyAddress = getProxyAddress(node);
        if (proxyAddress != null) {
            String moteUriResource = "";
            if (MoteResource.containsValue(resource)) {
                //moteUriResource += MoteResource.getMoteUriResource(resource);
                String theResourceName = MoteResource.getMoteUriResource(resource);
                if (theResourceName == null) {
                    logger.error("unsupported resource");
                    return UNDEFINED_COAP_MESSAGE_ID;
                }
                // FOR TCS adapter, we prefer the TEMPERATURE_TCS
                // FOR WLAB and HAI we prefer the TEMPERATURE_ALT
                // we do this check because the getMoteUriResource is making a reverse lookup in the hashmap (where two keys point to the same resource)
                if (theResourceName.compareToIgnoreCase(MoteResource.TEMPERATURE_TCS) == 0) {
                    theResourceName = MoteResource.TEMPERATURE_ALT;
                }
                moteUriResource += theResourceName;

                int packetID = UNDEFINED_COAP_MESSAGE_ID;
                do { //while loop to avoid a packetID that exists in the array that is to be ignored!
                    packetID = random.nextInt(65535) + 1;
                } while (timedOut_DTN_CoapMessageIDsList.contains(Integer.valueOf(packetID))
                        || packetID == UNDEFINED_COAP_MESSAGE_ID);

                String msgString = packetID + "#" + node.getId() + "#" + RESOURCE_REQ + "#" + moteUriResource;
                byte[] msgBytes = new byte[Constants.DTN_MESSAGE_SIZE];
                msgBytes = msgString.getBytes();
                DatagramPacket sendPacket = new DatagramPacket(msgBytes, msgBytes.length,
                        InetAddress.getByName(proxyAddress), Constants.PROXY_UDPFORWARDER_PORT);
                DatagramSocket clientSocket = new DatagramSocket();
                clientSocket.send(sendPacket);
                clientSocket.close();
                requestMsgId = packetID;
                logger.info("Sent Request: " + msgString);
            } else {
                logger.warn("No resource mapping for Node " + node.getId() + " and Resource " + resource.getName());
                throw new WSIAdapterException(
                        "No resource mapping for Node " + node.getId() + " and Resource " + resource.getName());
            }
        } else {
            logger.warn("No available proxy for Node " + node.getId() + " is found");
            throw new WSIAdapterException("No available proxy for Node " + node.getId() + " is found");
        }
        return requestMsgId;
    }

    /**
     * CoapClient interface
     */

    public void onConnectionFailed(CoapClientChannel channel, boolean notReachable, boolean resetByServer) {
        if (notReachable) {
            logger.warn("Connection failed: server is not reachable");
            exceptionError = "Connection failed: server is not reachable";
        } else {
            logger.warn("Connection failed");
            exceptionError = "Connection failed";
        }
        // signal.countDown(); //commented out because the message will timeout.
    }

    public void onResponse(CoapClientChannel channel, CoapResponse response) {
        boolean countDownTheSignal = false;
        try {
            if (!timedOutCoapMessageIDsList.contains(Integer.valueOf(response.getMessageID()))
                    && response.getMessageID() != UNDEFINED_COAP_MESSAGE_ID) {
                manageResponse(response);
                countDownTheSignal = true;
            }
            // TODO: if it is contained, do we remove it to clean up the list? (also the messageID for the coapMessages could be rotating, so we would need clean-up !!)
            //                          or do we expect further responses for a timeout message (due to the re-transmissions?)

        } catch (VitroGatewayException e) {
            logger.error(e.getMessage());
        }
        if (countDownTheSignal) {
            signal.countDown();
        }
    }

    public void onSeparateResponse(CoapClientChannel channel, CoapResponse message) {
        logger.info("Received Separate Response");
        //TODO: no implementation in TinyOS
        // signal.countDown(); //commented out because the message will timeout.
    }

    public void onSeparateResponseAck(CoapClientChannel channel, CoapEmptyMessage message) {
        logger.info("Received Ack of Separate Response");
        //TODO: no implementation in TinyOS
        // signal.countDown(); //commented out because the message will timeout.
    }

    /**
     * Private CoAP methods
     */

    private int coapResourcesRequest(Node node) throws UnknownHostException, WSIAdapterException {
        int messageIDToReturn = UNDEFINED_COAP_MESSAGE_ID;
        String proxyAddress = getProxyAddress(node);
        if (proxyAddress != null) {

            // 15/04
            CoapClientChannel clientChannelResources = null;
            try {
                // *** Alternative approach
                if (!proxyAddrToClientChannelResourcesHM.isEmpty()
                        && proxyAddrToClientChannelResourcesHM.containsKey(proxyAddress)
                        && proxyAddrToClientChannelResourcesHM.get(proxyAddress) != null) {
                    //re use it.
                    clientChannelResources = proxyAddrToClientChannelResourcesHM.get(proxyAddress);

                } else {
                    clientChannelResources = channelManager.connect((CoapClient) this,
                            InetAddress.getByName(proxyAddress), COAP_PORT);
                    if (clientChannelResources != null) {
                        proxyAddrToClientChannelResourcesHM.put(proxyAddress, clientChannelResources);
                    } else {
                        logger.error("Impossible to setup a coap channel for resources!");
                        return UNDEFINED_COAP_MESSAGE_ID;
                    }
                }
                // *** end of alternative approach

                // == Fix that was still causing too many open files:
                //if(clientChannelResources!= null) {
                //    clientChannelResources.close();
                //}
                //clientChannelResources = channelManager.connect(this, InetAddress.getByName(proxyAddress), COAP_PORT);
                // == end of fix
                //
                // clientChannelResources = channelManager.connect(this, InetAddress.getByName("localhost"), PORT);
                CoapRequest coapRequest = clientChannelResources.createRequest(true, CoapRequestCode.GET);
                coapRequest.setProxyUri("coap://[" + node.getId() + "]:61616/" + MoteResource.RESOURCE_DISCOVERY);
                //         coapRequest.setUriPath("/" + MoteResource.RESOURCE_DISCOVERY);
                clientChannelResources.sendMessage(coapRequest);
                messageIDToReturn = coapRequest.getMessageID();
                logger.info("Sent Request: {} for node {}", coapRequest.toString(), node.getId());
                //clientChannelResources.close();
            } catch (Exception ex) {
                logger.error("Could not setup a coap channel or send the coapResourcesRequest message!", ex);
                if (clientChannelResources != null) {
                    //    clientChannelResources.close();
                }
                throw new WSIAdapterException("Unable to send coap resource req to " + node.getId());
            } finally {
                if (clientChannelResources != null) {
                    //    clientChannelResources.close();
                }
            }
        } else {
            logger.warn("No available proxy for Node " + node.getId() + " is found");
            throw new WSIAdapterException("No available proxy for Node " + node.getId() + " is found");
        }
        return messageIDToReturn;
    }

    private int coapObservationRequest(Node node, Resource resource)
            throws UnknownHostException, VitroGatewayException {
        int messageIDToReturn = UNDEFINED_COAP_MESSAGE_ID;
        String proxyAddress = getProxyAddress(node);

        if (proxyAddress != null) {
            String moteUriResource = "";
            if (MoteResource.containsValue(resource)) {
                //moteUriResource += MoteResource.getMoteUriResource(resource);
                String theResourceName = MoteResource.getMoteUriResource(resource);
                if (theResourceName == null) {
                    logger.error("unsupported resource");
                    return UNDEFINED_COAP_MESSAGE_ID;
                }
                // FOR TCS adapter, we prefer the TEMPERATURE_TCS
                // FOR WLAB and HAI we prefer the TEMPERATURE_ALT
                // we do this check because the getMoteUriResource is making a reverse lookup in the hashmap (where two keys point to the same resource)
                if (theResourceName.compareToIgnoreCase(MoteResource.TEMPERATURE_TCS) == 0) {
                    theResourceName = MoteResource.TEMPERATURE_ALT;
                }
                moteUriResource += theResourceName;

                // 15/04
                CoapClientChannel clientChannelObservations = null;
                try {
                    // *** Alternative approach
                    if (!proxyAddrToClientChannelObservationsHM.isEmpty()
                            && proxyAddrToClientChannelObservationsHM.containsKey(proxyAddress)
                            && proxyAddrToClientChannelObservationsHM.get(proxyAddress) != null) {
                        //re use it.
                        clientChannelObservations = proxyAddrToClientChannelObservationsHM.get(proxyAddress);

                    } else {
                        clientChannelObservations = channelManager.connect((CoapClient) this,
                                InetAddress.getByName(proxyAddress), COAP_PORT);
                        if (clientChannelObservations != null) {
                            proxyAddrToClientChannelObservationsHM.put(proxyAddress, clientChannelObservations);
                        } else {
                            logger.error("Impossible to setup a coap channel for observations!");
                            return UNDEFINED_COAP_MESSAGE_ID;
                        }
                    }
                    // *** end of alternative approach

                    // == Fix that was still causing too many open files:
                    //if(clientChannelObservations!= null) {
                    //    clientChannelObservations.close();
                    //}
                    //clientChannelObservations = channelManager.connect(this, InetAddress.getByName(proxyAddress), COAP_PORT);
                    // == end of fix
                    //
                    //
                    // clientChannelObservations = channelManager.connect(this, InetAddress.getByName("localhost"), PORT);
                    CoapRequest coapRequest = clientChannelObservations.createRequest(true, CoapRequestCode.GET);
                    coapRequest.setProxyUri("coap://[" + node.getId() + "]:61616/" + moteUriResource);
                    //            coapRequest.setUriPath(moteUriResource);
                    clientChannelObservations.sendMessage(coapRequest);
                    messageIDToReturn = coapRequest.getMessageID();
                    logger.info("Sent Request: " + coapRequest.toString());
                    //clientChannelObservations.close();
                } catch (Exception ex) {
                    logger.error("Could not setup a coap channel or send the coapObservationRequest message!", ex);
                    if (clientChannelObservations != null) {
                        // clientChannelObservations.close();
                    }
                    throw new WSIAdapterException("Unable to send coap observe req to " + node.getId()
                            + " and Resource " + resource.getName());
                } finally {
                    if (clientChannelObservations != null) {
                        // clientChannelObservations.close();
                    }
                }
            } else {
                logger.warn("No resource mapping for Node " + node.getId() + " and Resource " + resource.getName());
                throw new WSIAdapterException(
                        "No resource mapping for Node " + node.getId() + " and Resource " + resource.getName());
            }
            //         
        } else {
            logger.warn("No available proxy for Node " + node.getId() + " is found");
            throw new WSIAdapterException("No available proxy for Node " + node.getId() + " is found");
        }
        return messageIDToReturn;
    }

    private String getProxyAddress(Node node) {
        String nodeIP = node.getId();

        if (nodeIP.contains(Network.WLAB_OFFICE_IPV6_PREFIX)
                || nodeIP.contains(Network.WLAB_OFFICE_IPV6_PREFIX_SHORT)) {
            return Network.WLAB_OFFICE_PROXY_ADDRESS;
        } else if (nodeIP.contains(Network.WLAB_LAB_IPV6_PREFIX)
                || nodeIP.contains(Network.WLAB_LAB_IPV6_PREFIX_SHORT)) {
            return Network.WLAB_LAB_PROXY_ADDRESS;
        }

        return null;
    }

    private void manageResponse(CoapResponse response) throws VitroGatewayException {
        logger.info("Received response code: " + response.getResponseCode());
        logger.info("CoAP Message ID: " + response.getMessageID());
        logger.info("Content Type: " + response.getContentType());
        logger.info("CoAP Server address: " + response.getChannel().getRemoteAddress());

        if (response.getPayload() == null) {
            return;
        }

        byte[] payloadBytes = response.getPayload();
        if (payloadBytes.length > 0) {
            logger.info("Content Type Val : " + Integer.toString(response.getContentType().getValue()));

            switch (response.getContentType().getValue()) {
            case 0: {
                manageTextPlain(payloadBytes); //textplain
                break;
            }
            case 40: {
                manageLinkFormat(payloadBytes); //linkformat --> /.well-known/core
                break;
            }
            case 41: //TODO: xml (required???)
                break;
            case 42: {
                manageOctetStream(payloadBytes); //octetstream --> resource outcomes
                break;
            }
            case 47: //TODO: exi (required???)
                break;
            case 50: //TODO: json (required???)
            default:
                logger.warn("Unknown Content Type");
            }
        } else {
            logger.warn("The payload is empty.");
        }
    }

    private void manageLinkFormat(byte[] payloadBytes) throws VitroGatewayException {

        String payloadString = new String(payloadBytes);

        /** 
         * Manage /.well-known/core resource 
         */

        // Pattern example: </st>;ct=42,</sh>;ct=42
        Pattern p = Pattern.compile("(</\\w+>);?(ct=\\d+)?", Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(payloadString);

        while (m.find()) {
            logger.debug("Regex Result: " + m.group());

            /** Resource Name */
            if (m.group(1) != null) {
                String resourceName = m.group(1).replaceAll("<|/|>", "");
                if (MoteResource.containsKey(resourceName)) {
                    logger.debug("Resource name: " + resourceName);
                    Resource resource = MoteResource.getResource(resourceName);
                    resourceList.add(resource);
                }
            }

            /** Resource Content Type */
            if (m.group(2) != null) {
                // Do Nothing: not necessary
            }
        }
    }

    private void manageTextPlain(byte[] payloadBytes) {
        String payloadString = new String(payloadBytes);
        resourceValue = payloadString;
    }

    private void manageOctetStream(byte[] payloadBytes) {
        if (payloadBytes.length == 2) {
            short payloadShort = Functions.byteArraytoShort(payloadBytes);
            resourceValue = String.valueOf(payloadShort);
            logger.debug("Resource value (2 bytes): " + resourceValue);
        } else {
            // CHANGED THIS TO REFLECT THE EXACT BYTES ARRAY AND BE ABLE TO RECONSTRUCT IT
            //resourceValue = new String(payloadBytes);
            resourceValue = Base64.encodeBase64String(payloadBytes);
            logger.debug("Resource value (" + payloadBytes.length + "): " + resourceValue);
        }
    }

    private String formatResourceValue(String resourceValue, Resource resource) {

        StringBuilder resultBld = new StringBuilder();
        String resultRet = "";
        resultBld.append(resourceValue);
        resultRet = resultBld.toString();

        int resourceValueLength = resultRet.length();
        String integerPart = resultRet.substring(0, resourceValueLength - 2);
        String decimalPart = resultRet.substring(resourceValueLength - 2, resourceValueLength);

        if (resource.getName().equals(Resource.PHENOMENOM_TEMPERATURE)
                || resource.getName().equals(Resource.PHENOMENOM_HUMIDITY)) {
            logger.debug(" Temperature or Humidity Value detected!");
            resultBld = new StringBuilder();
            resultBld.append(integerPart);
            resultBld.append(".");
            resultBld.append(decimalPart);

        }
        resultRet = resultBld.toString();
        logger.debug("Resource formatted value: " + resultRet);
        return resultRet;
    }

    // ------------------ Trust Coap messaging
    public boolean isTrustCoapMessagingActive() {
        return trustCoapMessagingActivated;
    }

    /**
     * is supposed to start the Trust Coap Messaging (switch on the Trust Coap Messaging mode)
     */
    public void setTrustCoapMessagingActive(boolean value) {
        trustCoapMessagingActivated = value;
    }

    /**
     * Used to get the coap response message for the trust routing resource from a node.  (the Resource parameter will always be Resource.RES_TRUST_ROUTING
     * @param node the  node queried
     * @param resource the resource queried (always Resource.RES_TRUST_ROUTING)
     * @return InfoOnTrustRouting object with the response
     */
    public InfoOnTrustRouting getNodeTrustRoutingInfo(Node node, Resource resource) throws WSIAdapterException {
        //    code reuses the getNodeObservation() method
        // this is experimental. The value that Coap trust-routing messages return has variable size - it is a list of 32bit entries
        //
        InfoOnTrustRouting retInfo = null;
        if (node != null && resource == null) {
            Observation theNodeTrustInfoObservation = null;
            try {
                theNodeTrustInfoObservation = getNodeObservation(node, resource);
            } catch (WSIAdapterException wsiex) {
                theNodeTrustInfoObservation = null;
                logger.error("Error while running getNodeObservation for Coap trust routing", wsiex);
            }
            if (theNodeTrustInfoObservation != null) {
                retInfo = new InfoOnTrustRouting();
                String tmpStr = theNodeTrustInfoObservation.getValue();
                byte[] wholePayloadBytes = Base64.decodeBase64(tmpStr);
                logger.debug("Trust Routing retrieval for node: " + node.getId() + " returned bytes of length: "
                        + wholePayloadBytes.length);

                retInfo.setSourceNodeId(node.getId());
                retInfo.setTimestamp(Long.toString(theNodeTrustInfoObservation.getTimestamp()));

                if (wholePayloadBytes != null && wholePayloadBytes.length >= 4) {
                    //String tmpNodeIdSubStr;
                    //String tmpPFIvalSubStr;
                    for (int i = 0; i < wholePayloadBytes.length && (i + 3 < wholePayloadBytes.length); i += 4) {
                        //tmpNodeIdSubStr = wholePayload.substring(i, i+2);
                        //tmpPFIvalSubStr = wholePayload.substring(i+2, i+4);
                        //logger.debug("TrustRoutingCoap par node: " +tmpNodeIdSubStr + " pfi val: " + tmpPFIvalSubStr);
                        byte[] nodeIdBytes = new byte[] { wholePayloadBytes[i], wholePayloadBytes[i + 1] }; //tmpNodeIdSubStr.getBytes();  //javax.xml.bind.DatatypeConverter.parseHexBinary(tmpNodeIdSubStr);
                        byte[] pfiValueHexBytes = new byte[] { wholePayloadBytes[i + 2], wholePayloadBytes[i + 3] }; // tmpPFIvalSubStr.getBytes(); //javax.xml.bind.DatatypeConverter.parseHexBinary(tmpPFIvalSubStr);
                        short tmpNodeIdValue = Functions.byteArraytoShort(nodeIdBytes);
                        // TODO we could set the prefix later
                        String parentNodeId = (new StringBuilder()).append(InfoOnTrustRouting.getNodePrefix())
                                .append(tmpNodeIdValue).toString();
                        int tmpPFIvalue = Functions.byteArraytoSecShort(pfiValueHexBytes);
                        logger.debug("TrustRoutingCoap par node Sec CONVERTED: " + parentNodeId + " pfi val: "
                                + Integer.valueOf(tmpPFIvalue).toString());
                        retInfo.getParentIdsToPFI().put(parentNodeId, Integer.valueOf(tmpPFIvalue));
                    }
                }
                if (retInfo.getSourceNodeId().compareTo(InfoOnTrustRouting.INVALID_SOURCENODEID) == 0
                        || retInfo.getParentIdsToPFI().isEmpty()) {
                    retInfo = null;
                }
            } else {
                logger.error("Null Observation was returned to method getNodeTrustRoutingInfo()");
            }
        } else {
            logger.error("Null node or resource was provided to method getNodeTrustRoutingInfo()");
        }
        return retInfo;
    }
}