blob: c2c257f7568bad73d8c0f94290ce1097d51016b2 [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.core.router;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.ResourceType;
import org.eclipse.om2m.commons.constants.ResponseStatusCode;
import org.eclipse.om2m.commons.constants.ResponseType;
import org.eclipse.om2m.commons.constants.ResultContent;
import org.eclipse.om2m.commons.constants.ShortName;
import org.eclipse.om2m.commons.exceptions.AccessDeniedException;
import org.eclipse.om2m.commons.exceptions.BadRequestException;
import org.eclipse.om2m.commons.exceptions.NotImplementedException;
import org.eclipse.om2m.commons.exceptions.Om2mException;
import org.eclipse.om2m.commons.exceptions.ResourceNotFoundException;
import org.eclipse.om2m.commons.resource.DynamicAuthorizationConsultation;
import org.eclipse.om2m.commons.resource.RequestPrimitive;
import org.eclipse.om2m.commons.resource.ResponsePrimitive;
import org.eclipse.om2m.core.controller.AEController;
import org.eclipse.om2m.core.controller.AccessControlPolicyController;
import org.eclipse.om2m.core.controller.AEAnncController;
import org.eclipse.om2m.core.controller.CSEBaseController;
import org.eclipse.om2m.core.controller.ContainerController;
import org.eclipse.om2m.core.controller.ContentInstanceController;
import org.eclipse.om2m.core.controller.Controller;
import org.eclipse.om2m.core.controller.DiscoveryController;
import org.eclipse.om2m.core.controller.DynamicAuthorizationConsultationController;
import org.eclipse.om2m.core.controller.FanOutPointController;
import org.eclipse.om2m.core.controller.FlexContainerAnncController;
import org.eclipse.om2m.core.controller.FlexContainerController;
import org.eclipse.om2m.core.controller.GroupController;
import org.eclipse.om2m.core.controller.LatestOldestController;
import org.eclipse.om2m.core.controller.LatestOldestController.SortingPolicy;
import org.eclipse.om2m.core.controller.MgmtObjAnncController;
import org.eclipse.om2m.core.controller.MgmtObjController;
import org.eclipse.om2m.core.controller.NodeAnncController;
import org.eclipse.om2m.core.controller.NodeController;
import org.eclipse.om2m.core.controller.PollingChannelController;
import org.eclipse.om2m.core.controller.PollingChannelUriController;
import org.eclipse.om2m.core.controller.RemoteCSEController;
import org.eclipse.om2m.core.controller.RequestController;
import org.eclipse.om2m.core.controller.SubscriptionController;
import org.eclipse.om2m.core.datamapper.DataMapperSelector;
import org.eclipse.om2m.core.nblocking.NonBlockingHandler;
import org.eclipse.om2m.core.redirector.Redirector;
import org.eclipse.om2m.core.service.CseService;
import org.eclipse.om2m.core.urimapper.UriMapper;
/**
* Routes a generic request to the appropriate resource controller to handle it
* based on the request method and URI.
*/
public class Router implements CseService {
/** Logger */
private static Log LOGGER = LogFactory.getLog(Router.class);
/**
* Invokes required resource controller method.
*
* @param request
* - The generic request to handle
* @return The generic returned response
*/
public ResponsePrimitive doRequest(RequestPrimitive request) {
LOGGER.info("Received request in Router: " + request.toString());
ResponsePrimitive response = new ResponsePrimitive(request);
Patterns patterns = new Patterns();
String contentFormat = System.getProperty("org.eclipse.om2m.registration.contentFormat", MimeMediaType.XML);
try {
// Non blocking request handling
if (request.getResponseTypeInfo() != null && request.getResponseTypeInfo().getResponseType() != null) {
if (request.getResponseTypeInfo().getResponseType().equals(ResponseType.NON_BLOCKING_REQUEST_SYNCH)
|| request.getResponseTypeInfo().getResponseType()
.equals(ResponseType.NON_BLOCKING_REQUEST_ASYNCH)) {
if (Constants.NON_BLOCKING_SUPPORTED) {
return NonBlockingHandler.handle(request);
} else {
response.setResponseStatusCode(ResponseStatusCode.NON_BLOCKING_REQUEST_NOT_SUPPORTED);
response.setContent("Non blocking request is not supported");
response.setContentType(MimeMediaType.TEXT_PLAIN);
return response;
}
}
}
// Check if the data type has been set
if (request.getRequestContentType() == null) {
request.setRequestContentType(contentFormat);
LOGGER.info("No Content-Type parameter set, setting to default: " + request.getRequestContentType());
}
if (request.getReturnContentType() == null) {
request.setReturnContentType(contentFormat);
LOGGER.info("No Accept parameter set, setting to default: " + request.getReturnContentType());
}
// Check if the data type requested is supported
if (request.getRequestContentType() != MimeMediaType.OBJ && request.getRequestContentType() != null) {
if (!DataMapperSelector.getDataMapperList().containsKey(request.getRequestContentType())) {
throw new NotImplementedException(request.getRequestContentType() + " is not supported.");
}
}
if (request.getReturnContentType() != MimeMediaType.OBJ && request.getReturnContentType() != null) {
if (!DataMapperSelector.getDataMapperList().containsKey(request.getReturnContentType())) {
throw new NotImplementedException(request.getReturnContentType() + " is not supported.");
}
}
// Check if the operation has been provided
if (request.getOperation() == null) {
throw new BadRequestException("No Operation provided");
}
// URI Handling
if (request.getTo() == null) {
throw new BadRequestException("No To/TargetId parameter provided");
}
if (request.getTo().startsWith("~")) {
request.setTo(request.getTo().substring(1));
}
// Check if the SP-ID is provided
if (request.getTo().startsWith("//") || request.getTo().startsWith("_")) {
String uri = request.getTo().substring(2);
String spId = uri.split("/")[0];
if (!spId.equals(Constants.M2M_SP_ID)) {
throw new ResourceNotFoundException("Not the current SP Domain (" + spId + ")");
} else {
request.setTo(uri.replace(spId, ""));
}
} else if (!request.getTo().startsWith("/")) {
request.setTo("/" + Constants.CSE_ID + "/" + request.getTo());
}
// Remove the last "/" from the request uri if exist.
if (request.getTo().endsWith("/")) {
request.setTo(request.getTo().substring(0, request.getTo().length() - 1));
}
getQueryStringFromTargetId(request);
// Redirection case
if (!patterns.match(patterns.NON_RETARGETING_PATTERN, request.getTo())) {
LOGGER.info("Request targeting another CSE, forwarding to Redirector: " + request.getTo());
return Redirector.retarget(request);
}
LOGGER.info("Request handling in the current CSE: " + request.getTo());
Controller controller = null;
// Case of hierarchical URI, retrieve the non-hierarchical URI of the resource
if (patterns.match(patterns.HIERARCHICAL_PATTERN, request.getTo())) {
if (request.getTo().contains(patterns.FANOUT_POINT_MATCH + "/")) {
int foptIndex = request.getTo().indexOf(patterns.FANOUT_POINT_MATCH);
String uri = request.getTo().substring(0, foptIndex);
String suffix = request.getTo().substring(foptIndex + patterns.FANOUT_POINT_MATCH.length(),
request.getTo().length());
controller = new FanOutPointController(suffix);
request.setTo(uri);
LOGGER.info("Fan Out request received: [grp uri: " + uri + ", suffix: " + suffix + "]");
}
if (request.getTo().endsWith(patterns.FANOUT_POINT_MATCH)) {
controller = new FanOutPointController();
request.setTo(request.getTo().replaceAll(patterns.FANOUT_POINT_MATCH, ""));
LOGGER.info("Fan Out request received: [grp uri: " + request.getTo() + "]");
}
if (request.getTo().endsWith("/" + ShortName.LATEST)) {
controller = new LatestOldestController(SortingPolicy.LATEST);
request.setTo(request.getTo() + "/");
request.setTo(request.getTo().replace("/" + ShortName.LATEST + "/", ""));
}
if (request.getTo().endsWith("/" + ShortName.OLDEST)) {
controller = new LatestOldestController(SortingPolicy.OLDEST);
request.setTo(request.getTo() + "/");
request.setTo(request.getTo().replace("/" + ShortName.OLDEST + "/", ""));
}
String nonHierarchicalUri = UriMapper.getNonHierarchicalUri(request.getTo());
if (nonHierarchicalUri == null) {
throw new ResourceNotFoundException("Resource not found");
}
request.setTo(nonHierarchicalUri);
LOGGER.debug("Changing to unstructured uri for routing to: " + request.getTo());
}
// Notify case
if (request.getOperation().equals(Operation.NOTIFY)) {
return Redirector.retargetNotify(request);
}
// Discovery case
if ((request.getFilterCriteria() != null) && (request.getFilterCriteria().getFilterUsage() != null)
&& (request.getFilterCriteria().getFilterUsage().intValue() == 1)) {
controller = new DiscoveryController();
}
// Determine the appropriate resource controller
// In case of a CREATE, the resource type determine the controller
if (controller == null) {
if (request.getOperation().equals(Operation.CREATE)) {
controller = getResourceControllerFromRT(request.getResourceType());
} else {
controller = getResourceControllerFromURI(request.getTo());
}
}
if (controller != null) {
LOGGER.info("ResourceController to be used [" + controller.getClass().getSimpleName() + "]");
// Perform the request in the specific controller
response = controller.doRequest(request);
if (request.getResultContent() != null && request.getResultContent().equals(ResultContent.NOTHING)) {
response.setContent(null);
} else {
if (response.getContent() != null && !(response.getContent() instanceof String)
&& !request.getReturnContentType().equals(MimeMediaType.OBJ)) {
String representation = DataMapperSelector.getDataMapperList()
.get(request.getReturnContentType()).objToString(response.getContent());
response.setContent(representation);
response.setContentType(request.getReturnContentType());
}
}
} else {
throw new BadRequestException("Malformed URI");
}
} catch (AccessDeniedException accessDeniedException) {
response.setResponseStatusCode(accessDeniedException.getErrorStatusCode());
response.setContent(accessDeniedException.getMessage());
response.setContentType(MimeMediaType.TEXT_PLAIN);
if (accessDeniedException.getDynAuthTokenReqInfo() != null) {
response.setContentType(request.getReturnContentType());
response.setContent(DataMapperSelector.getDataMapperList().get(request.getReturnContentType())
.objToString(accessDeniedException.getDynAuthTokenReqInfo()));
}
LOGGER.error("AccessDeniedException caught in Router: " + accessDeniedException.getMessage(),
accessDeniedException);
} catch (Om2mException om2mException) {
response.setResponseStatusCode(om2mException.getErrorStatusCode());
response.setContent(om2mException.getMessage());
response.setContentType(MimeMediaType.TEXT_PLAIN);
LOGGER.error("OM2M exception caught in Router: " + om2mException.getMessage(), om2mException);
LOGGER.debug("OM2M exception caught in Router", om2mException);
} catch (Exception e) {
LOGGER.error("Router internal error", e);
response.setResponseStatusCode(ResponseStatusCode.INTERNAL_SERVER_ERROR);
response.setContent("Router internal error");
response.setContentType(MimeMediaType.TEXT_PLAIN);
}
LOGGER.info("Response in Router= " + response);
return response;
}
/**
* Finds required resource controller based on uri patterns.
*
* @param uri
* - Generic request uri
* @param method
* - Generic request method
* @param representation
* - Resource representation
* @return The matched resource controller otherwise null
*/
protected Controller getResourceControllerFromURI(String uri) {
Patterns patterns = new Patterns();
// Match the resource controller with an uri pattern and return it, otherwise
// return null
if (patterns.match(patterns.CSE_BASE_PATTERN, uri)) {
return new CSEBaseController();
}
if (patterns.match(patterns.AE_PATTERN, uri)) {
return new AEController();
}
if (patterns.match(patterns.AEANNC_PATTERN, uri)) {
return new AEAnncController();
}
if (patterns.match(patterns.ACP_PATTERN, uri)) {
return new AccessControlPolicyController();
}
if (patterns.match(patterns.CONTAINER_PATTERN, uri)) {
return new ContainerController();
}
if (patterns.match(patterns.DYNAMIC_AUTHORIZATION_CONSULTATION_PATTERN, uri)) {
return new DynamicAuthorizationConsultationController();
}
if (patterns.match(patterns.FLEXCONTAINER_PATTERN, uri)) {
return new FlexContainerController();
}
if (patterns.match(patterns.FLEXCONTAINER_ANNC_PATTERN, uri)) {
return new FlexContainerAnncController();
}
if (patterns.match(patterns.CONTENTINSTANCE_PATTERN, uri)) {
return new ContentInstanceController();
}
if (patterns.match(patterns.REMOTE_CSE_PATTERN, uri)) {
return new RemoteCSEController();
}
if (patterns.match(patterns.GROUP_PATTERN, uri)) {
return new GroupController();
}
if (patterns.match(patterns.NODE_PATTERN, uri)) {
return new NodeController();
}
if (patterns.match(patterns.NODE_ANNC_PATTERN, uri)) {
return new NodeAnncController();
}
if (patterns.match(patterns.NMGMT_OBJ_PATTERN, uri)) {
return new MgmtObjController();
}
if (patterns.match(patterns.NMGMT_OBJ_ANNC_PATTERN, uri)) {
return new MgmtObjAnncController();
}
if (patterns.match(patterns.SUBSCRIPTION_PATTERN, uri)) {
return new SubscriptionController();
}
if (patterns.match(patterns.POLLING_CHANNEL_PATTERN, uri)) {
return new PollingChannelController();
}
if (patterns.match(patterns.POLLING_CHANNEL_URI_PATTERN, uri)) {
return new PollingChannelUriController();
}
if (patterns.match(patterns.REQUEST_PATTERN, uri)) {
return new RequestController();
}
return null;
}
/**
* In the case of a CREATE operation, the controller is determined by the
* resource type argument from the generic request primitive.
*
* @param resourceType
* @return the matching controller
*/
protected Controller getResourceControllerFromRT(BigInteger resourceType) {
// test in case resourceType is null
if (resourceType == null) {
throw new BadRequestException("Resource type is missing for creation request");
}
// switch case
switch (resourceType.intValue()) {
case ResourceType.CSE_BASE:
return new CSEBaseController();
case ResourceType.ACCESS_CONTROL_POLICY:
return new AccessControlPolicyController();
case ResourceType.AE:
return new AEController();
case ResourceType.CONTAINER:
return new ContainerController();
case ResourceType.CONTENT_INSTANCE:
return new ContentInstanceController();
case ResourceType.DYNAMIC_AUTHORIZATION_CONSULTATION:
return new DynamicAuthorizationConsultationController();
case ResourceType.REMOTE_CSE:
return new RemoteCSEController();
case ResourceType.GROUP:
return new GroupController();
case ResourceType.NODE:
return new NodeController();
case ResourceType.SUBSCRIPTION:
return new SubscriptionController();
case ResourceType.POLLING_CHANNEL:
return new PollingChannelController();
case ResourceType.FLEXCONTAINER:
return new FlexContainerController();
case ResourceType.AE_ANNC:
return new AEAnncController();
case ResourceType.FLEXCONTAINER_ANNC:
return new FlexContainerAnncController();
case ResourceType.MGMT_OBJ:
return new MgmtObjController();
case ResourceType.MGMT_OBJ_ANNC:
return new MgmtObjAnncController();
case ResourceType.NODE_ANNC:
return new NodeAnncController();
default:
throw new NotImplementedException("ResourceType: " + resourceType + " is not implemented");
}
}
private static void getQueryStringFromTargetId(RequestPrimitive request) {
if (request.getTo().contains("#")) {
request.getQueryStrings().put("#", new ArrayList());
}
if (request.getTo().contains("?")) {
String query = request.getTo().split("\\?")[1];
Map<String, List<String>> parameters = new HashMap<String, List<String>>();
if (query != null) {
String[] pairs = query.split("[&]");
for (String pair : pairs) {
String[] param = pair.split("[=]");
String key = null;
String value = null;
if (param.length > 0) {
key = param[0];
}
if (param.length > 1) {
value = param[1];
}
if (parameters.containsKey(key)) {
parameters.get(key).add(value);
} else {
List<String> values = new ArrayList<String>();
values.add(value);
parameters.put(key, values);
}
}
}
request.getQueryStrings().putAll(parameters);
}
}
}