blob: aa14ce73136e8bc32c1559f62a4850d69b663d7f [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.io.IOException;
import java.math.BigInteger;
import java.net.SocketException;
import java.util.Arrays;
import java.util.List;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.om2m.commons.constants.Constants;
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.FilterCriteria;
import org.eclipse.om2m.commons.resource.RequestPrimitive;
import org.eclipse.om2m.commons.resource.ResponsePrimitive;
import org.eclipse.om2m.commons.resource.ResponseTypeInfo;
import org.eclipse.om2m.commons.resource.StatusCode;
import org.eclipse.om2m.core.service.CseService;
import ch.ethz.inf.vs.californium.coap.CoAP;
import ch.ethz.inf.vs.californium.coap.CoAP.Code;
import ch.ethz.inf.vs.californium.coap.CoAP.ResponseCode;
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;
import ch.ethz.inf.vs.californium.network.Exchange;
import ch.ethz.inf.vs.californium.server.MessageDeliverer;
import ch.ethz.inf.vs.californium.server.Server;
/**
* Server side for CoAP binding
*
*/
public class CoapServer {
private Server server;
private CoapMessageDeliverer msgDeliverer;
private static int port = Constants.COAP_PORT;
/**
* Start the COAP server
* @throws Exception
*/
public void startServer() throws Exception {
server = new Server(port);
msgDeliverer = new CoapMessageDeliverer();
server.setMessageDeliverer(msgDeliverer);
server.start();
}
/**
* Stop the CoAP server
*/
public void stopServer(){
if(server != null){
server.stop();
}
}
}
class CoapMessageDeliverer implements MessageDeliverer {
private static Log LOGGER = LogFactory.getLog(CoapMessageDeliverer.class);
private static CseService cse;
@Override
public void deliverRequest(Exchange exchange) {
Request req = exchange.getRequest();
Response resp = null;
try {
resp = service(req);
LOGGER.info("Response to sent: " + resp);
} catch (SocketException e) {
LOGGER.error("the service failed! ", e);
} catch (IOException e) {
LOGGER.error("IOexception", e);
}
exchange.sendResponse(resp);
}
@Override
public void deliverResponse(Exchange rqst, Response rspns) {
rqst.sendResponse(rspns);
LOGGER.info("response= " + rspns);
}
/**
* Converts a {@link CoapServerRequest} to a {@link RequestIndication} and
* uses it to invoke the SCL service. Converts the received
* {@link ResponseConfirm} to a {@link CoapServerResponse} and returns it
* back.
*/
public Response service(Request request) throws SocketException,
IOException {
LOGGER.info("----------------------------------------------------------------------------------------------");
int mid = request.getMID();
byte[] token = request.getToken();
RequestPrimitive requestPrimitive = new RequestPrimitive();
ResponsePrimitive responsePrimitive = new ResponsePrimitive();
OptionSet options = request.getOptions();
LOGGER.info("Coap incoming URI: /" + request.getOptions().getURIPathString());
LOGGER.info(options.toString());
String targetId = options.getURIPathString();
if (targetId.endsWith("/")) {
targetId = targetId.substring(0, targetId.length() - 1);
}
if(targetId.startsWith("~/")){
targetId = targetId.replaceFirst("~/", "/");
} else if(targetId.startsWith("_/")) {
targetId = targetId.replaceFirst("_/", "//");
}
requestPrimitive.setTo(request.getURI());
requestPrimitive.setContent(request.getPayloadString());
// Parse CoAP options
List<Option> optionsList = options.asSortedList();
for (int i = 0; i < optionsList.size(); i++) {
switch (optionsList.get(i).getNumber()) {
case CoapOptions.ONEM2M_FR:
requestPrimitive.setFrom(optionsList.get(i).getStringValue());
break;
case CoapOptions.ONEM2M_RQI:
requestPrimitive.setRequestIdentifier(optionsList.get(i)
.getStringValue());
break;
case CoapOptions.ONEM2M_OT:
requestPrimitive.setOriginatingTimestamp(optionsList.get(i)
.getStringValue());
break;
case CoapOptions.ONEM2M_RQET:
requestPrimitive.setRequestExpirationTimestamp(optionsList.get(
i).getStringValue());
break;
case CoapOptions.ONEM2M_RSET:
requestPrimitive.setResultExpirationTimestamp(optionsList
.get(i).getStringValue());
break;
case CoapOptions.ONEM2M_OET:
requestPrimitive.setOperationExecutionTime(optionsList.get(i)
.getStringValue());
break;
case CoapOptions.ONEM2M_RTURI:
String[] uri = optionsList.get(i).getStringValue().split("&");
if(requestPrimitive.getResponseTypeInfo() == null){
requestPrimitive.setResponseTypeInfo(new ResponseTypeInfo());
}
requestPrimitive.getResponseTypeInfo()
.getNotificationURI().addAll(Arrays.asList(uri));
break;
case CoapOptions.ONEM2M_GID:
requestPrimitive.setGroupRequestIdentifier(optionsList.get(i)
.getStringValue());
break;
case CoapOptions.ONEM2M_TY:
requestPrimitive.setResourceType(new BigInteger(optionsList
.get(i).getValue()));
break;
}
}
LOGGER.info("URIQueries: " + options.getURIQueries());
mapParameters(request, requestPrimitive);
requestPrimitive.setOperation(getOneM2MOperation(request.getCode(),
requestPrimitive.getResourceType()));
if (options.getContentFormat() == 41) {
requestPrimitive.setRequestContentType(MimeMediaType.XML);
} else if (options.getContentFormat() == 50) {
requestPrimitive.setRequestContentType(MimeMediaType.JSON);
}
if (options.getAccept() == 41) {
requestPrimitive.setReturnContentType(MimeMediaType.XML);
} else if (options.getAccept() == 50) {
requestPrimitive.setReturnContentType(MimeMediaType.JSON);
}
if (cse != null) {
LOGGER.info("Execute requestPrimitive on the router");
responsePrimitive = cse.doRequest(requestPrimitive);
} else {
responsePrimitive
.setResponseStatusCode(ResponseStatusCode.SERVICE_UNAVAILABLE);
responsePrimitive.setContent("CSE service is not available.");
responsePrimitive.setContentType(MimeMediaType.TEXT_PLAIN);
}
ResponseCode statusCode = getCoapStatusCode(responsePrimitive
.getResponseStatusCode());
Response response = new Response(statusCode);
response.setMID(mid);
response.setToken(token);
if (request.getType().equals(CoAP.Type.CON)) {
response.setType(CoAP.Type.ACK);
}
if (responsePrimitive.getResponseStatusCode() != null) {
response.getOptions().addOption(
new Option(CoapOptions.ONEM2M_RSC,
responsePrimitive.getResponseStatusCode().intValue()));
}
if (responsePrimitive.getRequestIdentifier() != null) {
response.getOptions().addOption(
new Option(CoapOptions.ONEM2M_RQI,
responsePrimitive.getRequestIdentifier()));
}
if (responsePrimitive.getContent() != null) {
response.setPayload(responsePrimitive.getContent().toString());
}
if (responsePrimitive.getLocation() != null) {
response.getOptions().addOption(
new Option(8, responsePrimitive.getLocation()));
}
if(responsePrimitive.getContentType() != null){
if (responsePrimitive.getContentType().equals(
MimeMediaType.XML)) {
response.getOptions().setContentFormat(CoapContentType.APP_XML);
}
else if (responsePrimitive.getContentType().equals(MimeMediaType.XML_RESOURCE)){
response.getOptions().setContentFormat(CoapContentType.RES_XML);
}
else if(responsePrimitive.getContent().equals(MimeMediaType.JSON_RESOURCE)){
response.getOptions().setContentFormat(CoapContentType.RES_JSON);
}
else if (responsePrimitive.getContentType().equals(
MimeMediaType.JSON)) {
response.getOptions().setContentFormat(CoapContentType.APP_JSON);
} else if(responsePrimitive.getContentType().equals(MimeMediaType.TEXT_PLAIN)){
response.getOptions().setContentFormat(CoapContentType.PLAIN_TEXT);
}
}
return response;
}
/**
* Converts a {@link StatusCode} object into a standard CoAP status code .
*
* @param statusCode
* - protocol-independent status code.
* @param isEmptyBody
* - request body existence
* @return standard CoAP status code.
*/
public static ResponseCode getCoapStatusCode(BigInteger statusCode) {
if (statusCode.equals(ResponseStatusCode.OK)) {
return ResponseCode.CONTENT;
} else if (statusCode.equals(ResponseStatusCode.CREATED)) {
return ResponseCode.CREATED;
} else if (statusCode.equals(ResponseStatusCode.ACCEPTED)) {
return ResponseCode.VALID;
} else if (statusCode.equals(ResponseStatusCode.DELETED)) {
return ResponseCode.DELETED;
} else if (statusCode.equals(ResponseStatusCode.UPDATED)) {
return ResponseCode.CHANGED;
} else if (statusCode.equals(ResponseStatusCode.BAD_REQUEST)
|| statusCode.equals(ResponseStatusCode.CONTENTS_UNACCEPTABLE)
|| statusCode.equals(ResponseStatusCode.GROUP_REQUEST_IDENTIFIER_EXISTS)
|| statusCode
.equals(ResponseStatusCode.ALREADY_EXISTS)
|| statusCode.equals(ResponseStatusCode.MAX_NUMBER_OF_MEMBER_EXCEEDED)
|| statusCode
.equals(ResponseStatusCode.MEMBER_TYPE_INCONSISTENT)
|| statusCode.equals(ResponseStatusCode.INVALID_CMDTYPE)
|| statusCode.equals(ResponseStatusCode.INVALID_ARGUMENTS)
|| statusCode.equals(ResponseStatusCode.ALREADY_COMPLETED)
|| statusCode
.equals(ResponseStatusCode.COMMAND_NOT_CANCELLABLE)) {
return ResponseCode.BAD_REQUEST;
} else if (statusCode.equals(ResponseStatusCode.ACCESS_DENIED)
|| statusCode
.equals(ResponseStatusCode.SUBSCRIPTION_CREATOR_HAS_NO_PRIVILEGE)
|| statusCode.equals(ResponseStatusCode.NO_PRIVILEGE)
|| statusCode.equals(ResponseStatusCode.ALREADY_EXISTS)
|| statusCode.equals(ResponseStatusCode.CONFLICT)
|| statusCode
.equals(ResponseStatusCode.TARGET_NOT_SUBSCRIBABLE)
|| statusCode
.equals(ResponseStatusCode.SUBSCRIPTION_HOST_HAS_NO_PRIVILEGE)) {
return ResponseCode.FORBIDDEN;
} else if (statusCode.equals(ResponseStatusCode.NOT_FOUND)
|| statusCode.equals(ResponseStatusCode.REQUEST_TIMEOUT)
|| statusCode.equals(ResponseStatusCode.TARGET_NOT_REACHABLE)
|| statusCode
.equals(ResponseStatusCode.EXTERNAL_OBJECT_NOT_FOUND)
|| statusCode
.equals(ResponseStatusCode.EXTERNAL_OBJECT_NOT_REACHABLE)) {
return ResponseCode.NOT_FOUND;
} else if (statusCode.equals(ResponseStatusCode.OPERATION_NOT_ALLOWED)) {
return ResponseCode.METHOD_NOT_ALLOWED;
} else if (statusCode.equals(ResponseStatusCode.REQUEST_TIMEOUT)) {
return ResponseCode.SERVICE_UNAVAILABLE;
} else if (statusCode.equals(ResponseStatusCode.CONFLICT)
|| statusCode
.equals(ResponseStatusCode.GROUP_REQUEST_IDENTIFIER_EXISTS)) {
return ResponseCode.INTERNAL_SERVER_ERROR;
} else if (statusCode.equals(ResponseStatusCode.INTERNAL_SERVER_ERROR)
|| statusCode
.equals(ResponseStatusCode.SUBSCRIPTION_VERIFICATION_INITIATION_FAILED)
|| statusCode
.equals(ResponseStatusCode.MGMT_SESSION_CANNOT_BE_ESTABLISHED)
|| statusCode
.equals(ResponseStatusCode.MGMT_SESSION_ESTABLISHMENT_TIMEOUT)
|| statusCode.equals(ResponseStatusCode.MGMT_CONVERSION_ERROR)
|| statusCode
.equals(ResponseStatusCode.MGMT_CANCELATION_FAILURE)) {
return ResponseCode.INTERNAL_SERVER_ERROR;
} else if (statusCode.equals(ResponseStatusCode.NOT_IMPLEMENTED)
|| statusCode
.equals(ResponseStatusCode.NON_BLOCKING_REQUEST_NOT_SUPPORTED)) {
return ResponseCode.NOT_IMPLEMENTED;
} else if (statusCode.equals(ResponseStatusCode.SERVICE_UNAVAILABLE)) {
return ResponseCode.SERVICE_UNAVAILABLE;
}
return ResponseCode.SERVICE_UNAVAILABLE;
}
private static BigInteger getOneM2MOperation(Code code, BigInteger oneM2M_TY) {
BigInteger result = null;
switch (code) {
case GET:
return Operation.RETRIEVE;
case POST:
if (oneM2M_TY != null) {
result = Operation.CREATE;
} else {
result = Operation.NOTIFY;
}
return result;
case PUT:
return Operation.UPDATE;
case DELETE:
return Operation.DELETE;
default:
return null;
}
}
private void mapParameters(Request request, RequestPrimitive primitive) {
List<String> params = request.getOptions().getURIQueries();
String name, value;
FilterCriteria filterCriteria = new FilterCriteria();
for (int i = 0; i < params.size(); i++) {
if (params.get(i).split("=").length == 2) {
name = params.get(i).split("=")[0];
value = params.get(i).split("=")[1];
if (name.equals(CoapParameters.RESPONSE_TYPE)) {
if (primitive.getResponseTypeInfo() == null) {
primitive.setResponseTypeInfo(new ResponseTypeInfo());
}
primitive.getResponseTypeInfo().setResponseType(
new BigInteger(value));
}
if (name.equals(CoapParameters.RESULT_CONTENT)) {
primitive.setResultContent(new BigInteger(value));
}
if (name.equals(CoapParameters.RESULT_PERSISTENCE)) {
try {
Duration duration = DatatypeFactory.newInstance()
.newDuration(value);
primitive.setResultPersistence(duration);
} catch (DatatypeConfigurationException e) {
LOGGER.debug("Error in Duration creation", e);
}
}
if (name.equals(CoapParameters.DELIVERY_AGGREGATION)) {
primitive.setDeliveryAggregation(Boolean
.parseBoolean(value));
}
if (name.equals(CoapParameters.DISCOVERY_RESULT_TYPE)) {
primitive.setDiscoveryResultType(new BigInteger(value));
}
if (name.equals(CoapParameters.FILTER_USAGE)) {
filterCriteria.setFilterUsage(new BigInteger(value));
}
if (name.equals(CoapParameters.LIMIT)) {
filterCriteria.setLimit(new BigInteger(value));
}
if (name.equals(CoapParameters.LABELS)) {
filterCriteria.getLabels().add(value);
}
if (name.equals(CoapParameters.RESOURCE_TYPE)) {
filterCriteria.setResourceType(new BigInteger(value));
}
}
}
if (filterCriteria.getFilterUsage() != null) {
primitive.setFilterCriteria(filterCriteria);
}
}
public static CseService getCse() {
return cse;
}
public static void setCse(CseService cse) {
CoapMessageDeliverer.cse = cse;
}
}