com.sdl.odata.controller.AbstractODataController.java Source code

Java tutorial

Introduction

Here is the source code for com.sdl.odata.controller.AbstractODataController.java

Source

/**
 * Copyright (c) 2014 All Rights Reserved by the SDL Group.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sdl.odata.controller;

import com.sdl.odata.api.ODataException;
import com.sdl.odata.api.service.ODataRequest;
import com.sdl.odata.api.service.ODataResponse;
import com.sdl.odata.api.service.ODataService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Map;

import static com.sdl.odata.util.ReferenceUtil.isNullOrEmpty;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.springframework.web.bind.annotation.RequestMethod.PATCH;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;

/**
 * The abstract OData Controller.
 */
public abstract class AbstractODataController {
    private static final Logger LOG = LoggerFactory.getLogger(AbstractODataController.class);

    private static final int BUFFER_SIZE = 1024;
    private static final int DEFAULT_PORT_NUMBER = 80;
    private static final int DEFAULT_SSL_PORT_NUMBER = 443;

    @Autowired
    private ODataService oDataService;

    @RequestMapping(method = { GET, POST, PATCH, PUT, DELETE })
    protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
            throws ServletException, IOException {
        if (LOG.isTraceEnabled()) {
            LOG.trace("Start processing request from: {}", servletRequest.getRemoteAddr());
        }

        ODataResponse oDataResponse;
        try {
            ODataRequest oDataRequest = buildODataRequest(servletRequest);
            doWireLogging(oDataRequest);
            oDataResponse = oDataService.handleRequest(oDataRequest);
        } catch (ODataException e) {
            throw new ServletException(e);
        }

        fillServletResponse(oDataResponse, servletResponse);

        if (LOG.isTraceEnabled()) {
            LOG.trace("Finished processing request from: {}", servletRequest.getRemoteAddr());
        }
    }

    /**
     * Converts an {@code HttpServletRequest} to an {@code ODataRequest}.
     *
     * @param servletRequest The {@code HttpServletRequest}.
     * @return An {@code ODataRequest} containing the request information.
     * @throws java.io.IOException If an I/O error occurs.
     */
    private ODataRequest buildODataRequest(HttpServletRequest servletRequest) throws IOException {
        ODataRequest.Builder builder = new ODataRequest.Builder();

        builder.setMethod(ODataRequest.Method.valueOf(servletRequest.getMethod()));

        // Unfortunately, HttpServletRequest makes it difficult to get the full URI
        StringBuilder sb = getRequestURL(servletRequest);
        String queryString = servletRequest.getQueryString();
        if (!isNullOrEmpty(queryString)) {
            sb.append('?').append(queryString);
        }
        builder.setUri(sb.toString());

        // Unfortunately, HttpServletRequest has a very old-style API to iterate the headers
        Enumeration e = servletRequest.getHeaderNames();
        while (e.hasMoreElements()) {
            String name = (String) e.nextElement();
            String value = servletRequest.getHeader(name);
            builder.setHeader(name, value);
        }

        // Read the request body
        InputStream in = servletRequest.getInputStream();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int count;
        byte[] buffer = new byte[BUFFER_SIZE];
        while ((count = in.read(buffer)) != -1) {
            out.write(buffer, 0, count);
        }
        builder.setBody(out.toByteArray());

        return builder.build();
    }

    /**
     * In cases when {@link HttpServletRequest} is wrapped, request url will consist values from top wrapper now.
     * Instead of schema, port and server name from inner {@link org.apache.coyote.Request}.
     * Default case is when service is behind load balancer and {@link org.apache.catalina.filters.RemoteIpFilter}
     * is used for X-Forwarded headers.
     *
     * @param request wrapped/original request.
     * @return request URL based on values from wrapping Request.
     */
    private StringBuilder getRequestURL(HttpServletRequest request) {
        String scheme = request.getScheme();
        int port = request.getServerPort();
        if (port < 0) {
            port = DEFAULT_PORT_NUMBER;
        }

        StringBuilder url = new StringBuilder();
        url.append(scheme);
        url.append("://");
        url.append(request.getServerName());
        if ((scheme.equals("http") && (port != DEFAULT_PORT_NUMBER))
                || (scheme.equals("https") && (port != DEFAULT_SSL_PORT_NUMBER))) {
            url.append(':');
            url.append(port);
        }
        url.append(request.getRequestURI());
        return url;
    }

    /**
     * Transfers data from an {@code ODataResponse} into an {@code HttpServletResponse}.
     *
     * @param oDataResponse   The {@code ODataResponse}.
     * @param servletResponse The {@code HttpServletResponse}
     * @throws java.io.IOException If an I/O error occurs.
     */
    private void fillServletResponse(ODataResponse oDataResponse, HttpServletResponse servletResponse)
            throws IOException {
        servletResponse.setStatus(oDataResponse.getStatus().getCode());

        for (Map.Entry<String, String> entry : oDataResponse.getHeaders().entrySet()) {
            servletResponse.setHeader(entry.getKey(), entry.getValue());
        }

        byte[] body = oDataResponse.getBody();
        if (body != null && body.length != 0) {
            OutputStream out = servletResponse.getOutputStream();
            out.write(oDataResponse.getBody());
            out.flush();
        }
    }

    private void doWireLogging(ODataRequest request) throws UnsupportedEncodingException {
        if (LOG.isTraceEnabled()) {
            LOG.trace("RAW REQUEST LOGGING");

            LOG.trace("{} request for URL: {}", request.getMethod().name(), request.getUri());
            for (Map.Entry<String, String> headerEntry : request.getHeaders().entrySet()) {
                LOG.trace("Header: {} value: {}", headerEntry.getKey(), headerEntry.getValue());
            }

            LOG.trace("BODY: {}", request.getBodyText(UTF_8.name()));
        }
    }
}