org.eclipse.om2m.binding.http.RestHttpServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.om2m.binding.http.RestHttpServlet.java

Source

/*******************************************************************************
 * Copyright (c) 2013-2016 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 v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * 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
 *     Franois Assaoui : Developer
 *
 * New contributors :
 *******************************************************************************/
package org.eclipse.om2m.binding.http;

import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.binding.http.constants.HttpHeaders;
import org.eclipse.om2m.binding.http.constants.HttpParameters;
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.exceptions.BadRequestException;
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.commons.utils.Util;
import org.eclipse.om2m.core.service.CseService;

/**
 *  Provides mapping from a HTTP-specific request to a protocol-independent request.
 */
public class RestHttpServlet extends HttpServlet {
    /** Logger */
    private static Log LOGGER = LogFactory.getLog(RestHttpServlet.class);
    /** Serial Version UID */
    private static final long serialVersionUID = 1L;
    /** Discovered CSE service */
    private static CseService cse;

    /**
     * Converts a {@link HttpServletRequest} to a {@link RequestIndication} and uses it to invoke the SCL service.
     * Converts the received {@link ResponseConfirm} to a {@link HttpServletResponse} and returns it back.
     */
    @Override
    protected void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws ServletException, IOException {
        LOGGER.info(
                "----------------------------------------------------------------------------------------------");
        // Create generic request
        RequestPrimitive request = new RequestPrimitive();

        // Get the URI and split it to retrieve targetID
        String uri = httpServletRequest.getRequestURI();

        // Remove the context (ex: '/api')
        String targetID = uri;
        if (Activator.CSE_BASE_CONTEXT.length() > 1 && uri.length() > Activator.CSE_BASE_CONTEXT.length()) {
            targetID = uri.substring(Activator.CSE_BASE_CONTEXT.length());
        }

        if (targetID.startsWith("/~")) {
            targetID = targetID.replaceFirst("/~/", "/");
        } else if (targetID.startsWith("/_")) {
            targetID = targetID.replaceFirst("/_/", "//");
        } else if (targetID.startsWith("/")) {
            targetID = targetID.replaceFirst("/", "");
        }

        // Set the To parameter of the primitive request
        request.setTo(targetID);
        // Get the body of the request
        String content = null;
        try {
            if (httpServletRequest.getInputStream().available() > 0) {
                content = Util.convertStreamToString(httpServletRequest.getInputStream());
            }
        } catch (IOException e) {
            LOGGER.error("Error reading httpServletRequest InputStream", e);
        }
        if (content != null && !content.isEmpty()) {
            request.setContent(content);
        }

        // Get request parameters
        mapParameters(httpServletRequest, request);

        // Get the oneM2M operation
        LOGGER.info("Built RequestPrimitive: " + request.toString());
        try {
            request.setOperation(getOneM2MOperation(httpServletRequest));
        } catch (BadRequestException e) {
            httpServletResponse.setStatus(400);
            httpServletResponse.getWriter().print(e.getMessage());
            httpServletResponse.setContentType(MimeMediaType.TEXT_PLAIN);
            return;
        }

        mapHeaders(httpServletRequest, request);

        request.getQueryStrings().putAll(getParamsFromQuery(httpServletRequest.getQueryString()));

        // Perform the request in the CSE
        ResponsePrimitive response = null;
        if (cse != null) {
            response = cse.doRequest(request);
        } else {
            response = new ResponsePrimitive();
            response.setResponseStatusCode(ResponseStatusCode.SERVICE_UNAVAILABLE);
            response.setContent("CSE service is not available.");
            response.setContentType(MimeMediaType.TEXT_PLAIN);
        }

        // Set location header
        if (response.getLocation() != null) {
            httpServletResponse.setHeader(HttpHeaders.CONTENT_LOCATION, response.getLocation());
        }
        // Set the request identifier header
        if (response.getRequestIdentifier() != null) {
            httpServletResponse.setHeader(HttpHeaders.REQUEST_IDENTIFIER, response.getRequestIdentifier());
        }
        // Set the Origin header
        if (response.getFrom() != null) {
            httpServletResponse.setHeader(HttpHeaders.ORIGINATOR, response.getFrom());
        }

        // Set HTTP status code
        int httpStatus = getHttpStatusCode(response.getResponseStatusCode());
        httpServletResponse.setStatus(httpStatus);
        httpServletResponse.setHeader(HttpHeaders.RESPONSE_STATUS_CODE,
                response.getResponseStatusCode().toString());

        // Set the message body
        if (response.getContent() != null) {
            // Set the Content-Type header
            httpServletResponse.setContentType(response.getContentType());

            String body = (String) response.getContent();
            PrintWriter out = null;
            try {
                out = httpServletResponse.getWriter();
            } catch (IOException e) {
                LOGGER.error("Error reading httpServletResponse Writer", e);
            }
            out.println(body);
            out.close();
        }
    }

    /**
     * Return the oneM2M method from http request informations.
     * @param httpServletRequest
     * @return
     */
    private static BigInteger getOneM2MOperation(HttpServletRequest httpServletRequest) {
        BigInteger result = null;
        switch (httpServletRequest.getMethod()) {
        case "GET":
            return Operation.RETRIEVE;
        case "POST":
            if (httpServletRequest.getHeader(HttpHeaders.CONTENT_TYPE) != null
                    && httpServletRequest.getHeader(HttpHeaders.CONTENT_TYPE).contains("ty=")) {
                result = Operation.CREATE;
            } else {
                result = Operation.NOTIFY;
            }
            return result;
        case "PUT":
            return Operation.UPDATE;
        case "DELETE":
            return Operation.DELETE;
        default:
            throw new BadRequestException("HTTP Operation not supported: " + httpServletRequest.getMethod());
        }
    }

    /**
     * Converts a standard HTTP status code into a  {@link StatusCode} object.
     * @param statusCode - protocol-independent status code.
     * @param isEmptyBody - request body existence
     * @return standard HTTP status code.
     */
    public static int getHttpStatusCode(BigInteger statusCode) {
        if (statusCode.equals(ResponseStatusCode.OK) || statusCode.equals(ResponseStatusCode.UPDATED)
                || statusCode.equals(ResponseStatusCode.DELETED)) {
            return 200;
        } else if (statusCode.equals(ResponseStatusCode.CREATED)) {
            return 201;
        } else if (statusCode.equals(ResponseStatusCode.ACCEPTED)) {
            return 202;
        } else if (statusCode.equals(ResponseStatusCode.BAD_REQUEST)
                || statusCode.equals(ResponseStatusCode.CONTENTS_UNACCEPTABLE)
                || statusCode.equals(ResponseStatusCode.MAX_NUMBER_OF_MEMBER_EXCEEDED)
                || statusCode.equals(ResponseStatusCode.MEMBER_TYPE_INCONSISTENT)
                || statusCode.equals(ResponseStatusCode.INVALID_CMDTYPE)
                || statusCode.equals(ResponseStatusCode.INSUFFICIENT_ARGUMENTS)
                || statusCode.equals(ResponseStatusCode.ALREADY_COMPLETED)
                || statusCode.equals(ResponseStatusCode.COMMAND_NOT_CANCELLABLE)) {
            return 400;
        } 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.TARGET_NOT_SUBSCRIBABLE)
                || statusCode.equals(ResponseStatusCode.SUBSCRIPTION_HOST_HAS_NO_PRIVILEGE)) {
            return 403;
        } else if (statusCode.equals(ResponseStatusCode.NOT_FOUND)
                || statusCode.equals(ResponseStatusCode.TARGET_NOT_REACHABLE)
                || statusCode.equals(ResponseStatusCode.EXTERNAL_OBJECT_NOT_FOUND)
                || statusCode.equals(ResponseStatusCode.EXTERNAL_OBJECT_NOT_REACHABLE)) {
            return 404;
        } else if (statusCode.equals(ResponseStatusCode.OPERATION_NOT_ALLOWED)) {
            return 405;
        } else if (statusCode.equals(ResponseStatusCode.REQUEST_TIMEOUT)) {
            return 408;
        } else if (statusCode.equals(ResponseStatusCode.CONFLICT)
                || statusCode.equals(ResponseStatusCode.GROUP_REQUEST_IDENTIFIER_EXISTS)) {
            return 409;
        } 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 500;
        } else if (statusCode.equals(ResponseStatusCode.NOT_IMPLEMENTED)
                || statusCode.equals(ResponseStatusCode.NON_BLOCKING_REQUEST_NOT_SUPPORTED)) {
            return 501;
        } else if (statusCode.equals(ResponseStatusCode.SERVICE_UNAVAILABLE)) {
            return 503;
        }
        return 501;
    }

    /**
     * Get the registered cse
     * @return registered cse
     */
    public static CseService getScl() {
        return cse;
    }

    /**
     * Set the registered cse
     * @param cse to register
     */
    public static void setCse(CseService cse) {
        RestHttpServlet.cse = cse;
    }

    /**
     * Method used to map uri parameters to generic oneM2M request primitive.
     * @param request http request
     * @param primitive oneM2M generic request
     */
    private void mapParameters(HttpServletRequest request, RequestPrimitive primitive) {
        if (request.getParameter(HttpParameters.RESPONSE_TYPE) != null) {
            if (primitive.getResponseTypeInfo() == null) {
                primitive.setResponseTypeInfo(new ResponseTypeInfo());
            }
            primitive.getResponseTypeInfo()
                    .setResponseType(new BigInteger(request.getParameter(HttpParameters.RESPONSE_TYPE)));
            ;
        }
        if (request.getParameter(HttpParameters.RESULT_CONTENT) != null) {
            primitive.setResultContent(new BigInteger(request.getParameter(HttpParameters.RESULT_CONTENT)));
        }
        if (request.getParameter(HttpParameters.RESULT_PERSISTENCE) != null) {
            try {
                Duration duration = DatatypeFactory.newInstance()
                        .newDuration(request.getParameter(HttpParameters.RESULT_PERSISTENCE));
                primitive.setResultPersistence(duration);
            } catch (DatatypeConfigurationException e) {
                LOGGER.debug("Error in Duration creation", e);
            }
        }
        if (request.getParameter(HttpParameters.DELIVERY_AGGREGATION) != null) {
            primitive.setDeliveryAggregation(
                    Boolean.parseBoolean(request.getParameter(HttpParameters.DELIVERY_AGGREGATION)));
        }

        if (request.getParameter(HttpParameters.DISCOVERY_RESULT_TYPE) != null) {
            primitive.setDiscoveryResultType(
                    new BigInteger(request.getParameter(HttpParameters.DISCOVERY_RESULT_TYPE)));
        }

        if (request.getParameter(HttpParameters.FILTER_USAGE) != null) {
            FilterCriteria filterCriteria = new FilterCriteria();
            filterCriteria.setFilterUsage(new BigInteger(request.getParameter(HttpParameters.FILTER_USAGE)));
            if (request.getParameter(HttpParameters.LIMIT) != null) {
                filterCriteria.setLimit(new BigInteger(request.getParameter(HttpParameters.LIMIT)));
            }
            if (request.getParameter(HttpParameters.LABELS) != null) {
                filterCriteria.getLabels().addAll(Arrays.asList(request.getParameterValues(HttpParameters.LABELS)));
            }
            if (request.getParameter(HttpParameters.RESOURCE_TYPE) != null) {
                filterCriteria.setResourceType(new BigInteger(request.getParameter(HttpParameters.RESOURCE_TYPE)));
            }
            primitive.setFilterCriteria(filterCriteria);
        }
    }

    private void mapHeaders(HttpServletRequest httpServletRequest, RequestPrimitive request) {
        // Get the originator from the X-M2M-Origin header

        String authorization = httpServletRequest.getHeader(HttpHeaders.ORIGINATOR);
        if (authorization != null) {
            request.setFrom(authorization);
        }

        // Get the request identifier
        String requestIdentifier = httpServletRequest.getHeader(HttpHeaders.REQUEST_IDENTIFIER);
        if (requestIdentifier != null) {
            request.setRequestIdentifier(requestIdentifier.trim());
        }

        // Get Request Content type
        String contentTypeHeaders = httpServletRequest.getHeader(HttpHeaders.CONTENT_TYPE);
        if (contentTypeHeaders != null) {
            LOGGER.info("Content type headers: " + contentTypeHeaders);
            for (String contentTypeHeader : contentTypeHeaders.split(";")) {
                LOGGER.info("Header value: " + contentTypeHeader);
                if (contentTypeHeader.trim().startsWith("ty=")) {
                    String resourceType = contentTypeHeader.trim().split("ty=")[1];
                    request.setResourceType(new BigInteger(resourceType));
                }
                switch (contentTypeHeader.trim()) {
                case MimeMediaType.XML:
                case MimeMediaType.XML_RESOURCE:
                    request.setRequestContentType(MimeMediaType.XML);
                    break;
                case MimeMediaType.JSON:
                case MimeMediaType.JSON_RESOURCE:
                    request.setRequestContentType(MimeMediaType.JSON);
                    break;
                default:
                    break;
                }
            }
        } else {
            request.setRequestContentType(MimeMediaType.XML);
        }

        if (request.getRequestContentType() == null) {
            request.setRequestContentType(MimeMediaType.XML);
        }

        // Get dataTypes
        String acceptHeaders = httpServletRequest.getHeader(HttpHeaders.ACCEPT);
        LOGGER.info("Accept header: " + acceptHeaders);
        if (acceptHeaders != null) {
            for (String acceptHeader : acceptHeaders.split(";")) {
                switch (acceptHeader.trim()) {
                case MimeMediaType.XML:
                case MimeMediaType.XML_RESOURCE:
                    request.setReturnContentType(MimeMediaType.XML);
                    break;
                case MimeMediaType.JSON:
                case MimeMediaType.JSON_RESOURCE:
                    request.setReturnContentType(MimeMediaType.JSON);
                    break;
                default:
                    break;
                }
            }
        } else {
            request.setReturnContentType(request.getRequestContentType());
        }

        if (request.getReturnContentType() == null) {
            request.setReturnContentType(request.getRequestContentType());
        }

        // Map name header
        String nameHeader = httpServletRequest.getHeader(HttpHeaders.NAME);
        if (nameHeader != null) {
            request.setName(nameHeader);
        }

        // Map Response Type Uri for non-blocking request
        String rtuHeader = httpServletRequest.getHeader(HttpHeaders.RESPONSE_TYPE);
        if (rtuHeader != null) {
            LOGGER.info("Response Type URI header: " + rtuHeader);
            if (request.getResponseTypeInfo() == null) {
                request.setResponseTypeInfo(new ResponseTypeInfo());
            }
            for (String notifUri : rtuHeader.split("&")) {
                request.getResponseTypeInfo().getNotificationURI().add(notifUri.trim());
            }
        }

    }

    public static String httpRequestToString(HttpServletRequest request, String content) {
        String heads = "{\n";
        Enumeration<String> headerNames = request.getHeaderNames();

        while (headerNames.hasMoreElements()) {

            String headerName = headerNames.nextElement();
            heads += "\t" + (headerName) + ": ";

            Enumeration<String> headers = request.getHeaders(headerName);
            while (headers.hasMoreElements()) {
                String headerValue = headers.nextElement();
                heads += headerValue + "\n";
            }
        }
        heads += "}";
        return "HttpRequest [method=" + request.getMethod() + ", URI=" + request.getRequestURI()
                + ", representation=" + content + ", queryString=" + request.getQueryString() + ", headers :\n"
                + heads + "]";
    }

    public static String httpResponseToString(int statusCode, String errorMessage) {
        return "HttpResponse [statusCode=" + statusCode + ", errorMessage=" + errorMessage + "]";
    }

    /**
     * Converts a standard HTTP query String into a protocol independent parameters map.
     * @param query - standard HTTP query String.
     * @return protocol independent parameters map.
     */
    public static Map<String, List<String>> getParamsFromQuery(String query) {
        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);
                }
            }
        }
        return parameters;
    }

}