Java tutorial
/* * #-------------------------------------------------------------------------- * # 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; } }