blob: c2f6ac11fb50554784ea79aa99f877058572f3d2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013-2020 LAAS-CNRS (www.laas.fr)
* 7 Colonel Roche 31077 Toulouse - France
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Initial Contributors:
* Thierry Monteil : Project manager, technical co-manager
* Mahdi Ben Alaya : Technical co-manager
* Samir Medjiah : Technical co-manager
* Khalil Drira : Strategy expert
* Guillaume Garzone : Developer
* François Aïssaoui : Developer
*
* New contributors :
*******************************************************************************/
package org.eclipse.om2m.binding.coap;
import java.math.BigInteger;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.om2m.binding.service.RestClientService;
import org.eclipse.om2m.commons.constants.MimeMediaType;
import org.eclipse.om2m.commons.constants.Operation;
import org.eclipse.om2m.commons.constants.ResponseStatusCode;
import org.eclipse.om2m.commons.resource.Attribute;
import org.eclipse.om2m.commons.resource.FilterCriteria;
import org.eclipse.om2m.commons.resource.RequestPrimitive;
import org.eclipse.om2m.commons.resource.ResponsePrimitive;
import org.eclipse.om2m.commons.resource.StatusCode;
import ch.ethz.inf.vs.californium.coap.CoAP;
import ch.ethz.inf.vs.californium.coap.MediaTypeRegistry;
import ch.ethz.inf.vs.californium.coap.Option;
import ch.ethz.inf.vs.californium.coap.OptionSet;
import ch.ethz.inf.vs.californium.coap.Request;
import ch.ethz.inf.vs.californium.coap.Response;
public class CoapClient implements RestClientService {
/** Logger: */
private static Log LOGGER = LogFactory.getLog(CoapClient.class);
/** implemented specific protocol name */
private static String protocol = "coap";
/**
* gets the implemented specific protocol name
*
* @return protocol name
*/
public String getProtocol() {
return protocol;
}
/**
* Converts a protocol-independent {@link RequestIndication} object into a
* standard CoAP request and sends a standard CoAP request. Converts the
* received standard CoAP response into {@link ResponseConfirm} object and
* returns it back.
*
* @param requestIndication
* - protocol independent request.
* @return protocol independent response.
*/
public ResponsePrimitive sendRequest(RequestPrimitive requestPrimitive) {
LOGGER.debug("Sending request with CoAP binding: " +requestPrimitive);
// Retrieve the url
String url = requestPrimitive.getTo();
if(!url.startsWith(protocol+"://")){
if (url.startsWith("://")){
url = protocol + url;
} else if (url.startsWith("//")){
url = protocol + ":" + url;
} else {
url = protocol + "://" + url ;
}
}
// create the standard final response
ResponsePrimitive responsePrimitive = new ResponsePrimitive();
// get the Payload from requestIndication
String representation = requestPrimitive.getContent() != null ?
requestPrimitive.getContent().toString() :
null;
// create the code in order to create a coap request
CoAP.Code code = null;
// get the method from requestIndication
BigInteger operation = requestPrimitive.getOperation();
if (operation != null){
if (operation.equals(Operation.CREATE)){
code = CoAP.Code.POST;
} else if (operation.equals(Operation.RETRIEVE)
|| operation.equals(Operation.DISCOVERY)){
code = CoAP.Code.GET;
} else if (operation.equals(Operation.UPDATE)){
code = CoAP.Code.PUT;
} else if (operation.equals(Operation.DELETE)){
code = CoAP.Code.DELETE;
} else if (operation.equals(Operation.NOTIFY)){
code = CoAP.Code.POST;
}
} else {
return responsePrimitive;
}
// create a coap request
Request request = new Request(code);
// get the options of the CoAP request (which is still empty)
OptionSet options = new OptionSet();
request.setOptions(options);
// set the CoAP message Id
int MId = (int) (1000 + Math.random() * (9001));
request.setMID(MId);
// request.setToken(token);
CoAP.Type coapType = CoAP.Type.CON;
request.setType(coapType);
// set the request URI
request.setURI(url);
// set the payload
if(representation != null){
request.setPayload(representation);
}
// set the content format of the request
if(requestPrimitive.getRequestContentType() != null){
switch(requestPrimitive.getRequestContentType()){
case MimeMediaType.XML:
request.getOptions().setContentFormat(CoapContentType.APP_XML);
break;
case MimeMediaType.XML_RESOURCE:
request.getOptions().setContentFormat(CoapContentType.RES_XML);
break;
case MimeMediaType.JSON:
request.getOptions().setContentFormat(CoapContentType.APP_JSON);
break;
case MimeMediaType.JSON_RESOURCE:
request.getOptions().setContentFormat(CoapContentType.RES_JSON);
default:
break;
}
}
// set the accept of the request
if(requestPrimitive.getReturnContentType() != null){
switch(requestPrimitive.getReturnContentType()){
case MimeMediaType.XML:
request.getOptions().setAccept(CoapContentType.APP_XML);
break;
case MimeMediaType.XML_RESOURCE:
request.getOptions().setAccept(CoapContentType.RES_XML);
break;
case MimeMediaType.JSON:
request.getOptions().setAccept(CoapContentType.APP_JSON);
break;
case MimeMediaType.JSON_RESOURCE:
request.getOptions().setAccept(CoapContentType.RES_JSON);
default:
break;
}
}
// Set multiple parameters from the request primitive
if(requestPrimitive.getFrom() != null){
options.addOption(new Option(CoapOptions.ONEM2M_FR, requestPrimitive.getFrom()));
}
if(requestPrimitive.getRequestIdentifier() != null){
options.addOption(new Option(CoapOptions.ONEM2M_RQI, requestPrimitive.getRequestIdentifier()));
}
if(requestPrimitive.getResourceType() != null){
options.addOption(new Option(CoapOptions.ONEM2M_TY, requestPrimitive.getResourceType().intValue()));
}
if(requestPrimitive.getEventCategory() != null){
options.addOption(new Option(CoapOptions.ONEM2M_EC,
requestPrimitive.getEventCategory()));
}
if(!requestPrimitive.getQueryStrings().isEmpty()){
for(String queryStringKey : requestPrimitive.getQueryStrings().keySet()){
for(String value : requestPrimitive.getQueryStrings().get(queryStringKey)){
options.addURIQuery(queryStringKey + "=" + value);
}
}
}
if(requestPrimitive.getResponseTypeInfo() != null){
if(!requestPrimitive.getResponseTypeInfo().getNotificationURI().isEmpty()){
String notifUris = "";
for(String nu : requestPrimitive.getResponseTypeInfo().getNotificationURI()){
notifUris += nu + "&";
}
if(notifUris.endsWith("&")){
notifUris = notifUris.substring(0, notifUris.length() - 1);
}
options.addOption(new Option(CoapOptions.ONEM2M_RTURI, notifUris));
}
if(requestPrimitive.getResponseTypeInfo().getResponseType() != null){
options.addURIQuery(CoapParameters.RESPONSE_TYPE + "=" +
requestPrimitive.getResponseTypeInfo().getResponseType());
}
}
if(requestPrimitive.getResultPersistence() != null){
options.addURIQuery(CoapParameters.RESULT_PERSISTENCE + "="
+ requestPrimitive.getResultPersistence());
}
if(requestPrimitive.getResultContent() != null){
options.addURIQuery(CoapParameters.RESULT_CONTENT + "="
+ requestPrimitive.getResultContent());
}
if(requestPrimitive.getDiscoveryResultType() != null){
options.addURIQuery(CoapParameters.DISCOVERY_RESULT_TYPE + "="
+ requestPrimitive.getDiscoveryResultType());
}
// Set the filter criteria parameters
if(requestPrimitive.getFilterCriteria() != null){
FilterCriteria filter = requestPrimitive.getFilterCriteria();
if(!filter.getAttribute().isEmpty()){
for(Attribute att : filter.getAttribute()){
options.addURIQuery(att.getName() + "=" + att.getValue());
}
}
if(filter.getFilterUsage() != null){
options.addURIQuery(CoapParameters.FILTER_USAGE + "=" + filter.getFilterUsage());
}
if(!filter.getLabels().isEmpty()){
for(String label : filter.getLabels()){
options.addURIQuery(CoapParameters.LABELS + "=" + label);
}
}
if(filter.getLimit() != null){
options.addURIQuery(CoapParameters.LIMIT + "=" + filter.getLimit());
}
if(filter.getResourceType() != null){
options.addURIQuery(CoapParameters.RESOURCE_TYPE + "=" + filter.getResourceType());
}
}
if(requestPrimitive.getGroupRequestIdentifier() != null){
options.addOption(new Option(CoapOptions.ONEM2M_GID,
requestPrimitive.getGroupRequestIdentifier()));
}
// send the request
request.setScheme(url);
request.send();
// get the response
Response response = null;
try {
response = request.waitForResponse();
} catch (InterruptedException e) {
LOGGER.error("CoAP Client > Failed to receive response: " + e.getMessage(), e);
responsePrimitive.setResponseStatusCode(ResponseStatusCode.TARGET_NOT_REACHABLE);
responsePrimitive.setContent("Target is not reachable");
responsePrimitive.setContentType(MimeMediaType.TEXT_PLAIN);
return responsePrimitive;
}
if (response != null) {
if (response.getOptions().hasContentFormat(
MediaTypeRegistry.APPLICATION_LINK_FORMAT)) {
String linkFormat = response.getPayloadString();
// fill in the representation of the responsePrimitive
responsePrimitive.setContent(linkFormat);
} else {
responsePrimitive.setContent(response.getPayloadString());
}
}
// Parse response options
List<Option> optionsList = response.getOptions().asSortedList();
for(Option o : optionsList){
switch(o.getNumber()){
case CoapOptions.ONEM2M_FR:
responsePrimitive.setFrom(o.getStringValue());
break;
case CoapOptions.ONEM2M_RQI:
responsePrimitive.setRequestIdentifier(o.getStringValue());
break;
case CoapOptions.ONEM2M_RSC:
responsePrimitive.setResponseStatusCode(BigInteger.valueOf(o.getIntegerValue()));
break;
case CoapOptions.LOCATION:
responsePrimitive.setLocation(o.getStringValue());
break;
default:
LOGGER.trace("Option not handled: " + o.getNumber());
}
}
if(responsePrimitive.getResponseStatusCode() == null){
responsePrimitive.setResponseStatusCode(getResponseStatusCode(response.getCode().value));
}
switch(response.getOptions().getContentFormat()){
case CoapContentType.APP_XML:
responsePrimitive.setContentType(MimeMediaType.XML);
break;
case CoapContentType.RES_XML:
responsePrimitive.setContentType(MimeMediaType.XML_RESOURCE);
break;
case CoapContentType.APP_JSON:
responsePrimitive.setContentType(MimeMediaType.JSON);
break;
case CoapContentType.RES_JSON:
responsePrimitive.setContentType(MimeMediaType.JSON_RESOURCE);
}
LOGGER.debug("CoAP Client > "+responsePrimitive);
return responsePrimitive;
}
/**
* Converts a standard CoAP status code into a protocol-independent
* {@link StatusCode} object.
*
* @param statusCode
* - standard CoAP status code.
* @return protocol independent status.
*/
private BigInteger getResponseStatusCode(int statusCode) {
switch(statusCode){
case 200: return ResponseStatusCode.OK;
case 202: return ResponseStatusCode.ACCEPTED;
case 201: return ResponseStatusCode.CREATED;
case 204: return ResponseStatusCode.DELETED;
case 400: return ResponseStatusCode.BAD_REQUEST;
case 403: return ResponseStatusCode.ACCESS_DENIED;
case 404: return ResponseStatusCode.NOT_FOUND;
case 405: return ResponseStatusCode.OPERATION_NOT_ALLOWED;
case 409: return ResponseStatusCode.CONFLICT;
case 500: return ResponseStatusCode.INTERNAL_SERVER_ERROR;
case 501: return ResponseStatusCode.NOT_IMPLEMENTED;
case 503: return ResponseStatusCode.SERVICE_UNAVAILABLE;
default: return ResponseStatusCode.INTERNAL_SERVER_ERROR;
}
}
}