<commit_before>/*
 * Copyright (C) 2014 Dell, Inc.
 * 
 * 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.dell.doradus.service.rest;

import java.io.IOException;
import java.util.ArrayList;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dell.doradus.common.ApplicationDefinition;
import com.dell.doradus.common.HttpCode;
import com.dell.doradus.common.HttpDefs;
import com.dell.doradus.common.Pair;
import com.dell.doradus.common.RESTResponse;
import com.dell.doradus.common.Utils;
import com.dell.doradus.service.db.DBNotAvailableException;
import com.dell.doradus.service.db.DuplicateException;
import com.dell.doradus.service.db.Tenant;
import com.dell.doradus.service.db.UnauthorizedException;
import com.dell.doradus.service.schema.SchemaService;
import com.dell.doradus.service.tenant.TenantService;

/**
 * An HttpServlet implementation used by the {@link RESTService} to process REST requests.
 * Each request is mapped to a {@link RESTCommand} and processed via its callback. If the
 * the request is unknown or the callback creates 
 *
 */
public class RESTServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private final Logger m_logger = LoggerFactory.getLogger(getClass().getSimpleName());
    
    //----- Inherited methods from HttpServlet
    
    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            long startNano = System.nanoTime();
            RESTService.instance().onNewrequest();
            RESTResponse restResponse = validateAndExecuteRequest(request);
            if (restResponse.getCode().getCode() >= 300) {
                RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            } else {
                RESTService.instance().onRequestSuccess(startNano);
            }
            sendResponse(response, restResponse);
            m_logger.debug("Elapsed time: {} millis; request={}",
                           (float)(System.nanoTime() - startNano)/1000000, getFullURI(request));
        } catch (IllegalArgumentException e) {
            // 400 Bad Request
            RESTResponse restResponse = new RESTResponse(HttpCode.BAD_REQUEST, e.getMessage());
            m_logger.info("Returning client error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (NotFoundException e) {
            // 404 Not Found
            RESTResponse restResponse = new RESTResponse(HttpCode.NOT_FOUND, e.getMessage());
            m_logger.info("Returning client error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (DBNotAvailableException e) {
            // 503 Service Unavailable
            RESTResponse restResponse = new RESTResponse(HttpCode.SERVICE_UNAVAILABLE, e.getMessage());
            m_logger.info("Returning service error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (UnauthorizedException e) {
            // 401 Unauthorized
            RESTResponse restResponse = new RESTResponse(HttpCode.UNAUTHORIZED, e.getMessage());
            m_logger.info("Returning client error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (DuplicateException e) {
            // 409 Conflict
            RESTResponse restResponse = new RESTResponse(HttpCode.CONFLICT, e.getMessage());
            m_logger.info("Returning client error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (Throwable e) {
            // 500 Internal Error: include a stack trace and report in log.
            m_logger.error("Unexpected exception handling request: " + getFullURI(request), e);
            String stackTrace = Utils.getStackTrace(e);
            RESTResponse restResponse = new RESTResponse(HttpCode.INTERNAL_ERROR, stackTrace);
            RESTService.instance().onRequestFailed(e);
            sendResponse(response, restResponse);
        }
    }    // goGet
    
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    // doPost
    
    /**
     * @see HttpServlet#doPut(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    // doPut
    
    /**
     * @see HttpServlet#doDelete(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    // doDelete
    
    //----- Private methods

    // Execute the given request and return a RESTResponse or throw an appropriate error.
    private RESTResponse validateAndExecuteRequest(HttpServletRequest request) {
        Map<String, String> variableMap = new HashMap<String, String>();
        String query = extractQueryParam(request, variableMap);
        Tenant tenant = getTenant(variableMap);
        String uri = request.getPathInfo();<commit_msg>Fix URI processing for URLs that have encoded nodes.<commit_after>/*
 * Copyright (C) 2014 Dell, Inc.
 * 
 * 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.dell.doradus.service.rest;

import java.io.IOException;
import java.util.ArrayList;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dell.doradus.common.ApplicationDefinition;
import com.dell.doradus.common.HttpCode;
import com.dell.doradus.common.HttpDefs;
import com.dell.doradus.common.Pair;
import com.dell.doradus.common.RESTResponse;
import com.dell.doradus.common.Utils;
import com.dell.doradus.service.db.DBNotAvailableException;
import com.dell.doradus.service.db.DuplicateException;
import com.dell.doradus.service.db.Tenant;
import com.dell.doradus.service.db.UnauthorizedException;
import com.dell.doradus.service.schema.SchemaService;
import com.dell.doradus.service.tenant.TenantService;

/**
 * An HttpServlet implementation used by the {@link RESTService} to process REST requests.
 * Each request is mapped to a {@link RESTCommand} and processed via its callback. If the
 * the request is unknown or the callback creates 
 *
 */
public class RESTServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private final Logger m_logger = LoggerFactory.getLogger(getClass().getSimpleName());
    
    //----- Inherited methods from HttpServlet
    
    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            long startNano = System.nanoTime();
            RESTService.instance().onNewrequest();
            RESTResponse restResponse = validateAndExecuteRequest(request);
            if (restResponse.getCode().getCode() >= 300) {
                RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            } else {
                RESTService.instance().onRequestSuccess(startNano);
            }
            sendResponse(response, restResponse);
            m_logger.debug("Elapsed time: {} millis; request={}",
                           (float)(System.nanoTime() - startNano)/1000000, getFullURI(request));
        } catch (IllegalArgumentException e) {
            // 400 Bad Request
            RESTResponse restResponse = new RESTResponse(HttpCode.BAD_REQUEST, e.getMessage());
            m_logger.info("Returning client error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (NotFoundException e) {
            // 404 Not Found
            RESTResponse restResponse = new RESTResponse(HttpCode.NOT_FOUND, e.getMessage());
            m_logger.info("Returning client error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (DBNotAvailableException e) {
            // 503 Service Unavailable
            RESTResponse restResponse = new RESTResponse(HttpCode.SERVICE_UNAVAILABLE, e.getMessage());
            m_logger.info("Returning service error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (UnauthorizedException e) {
            // 401 Unauthorized
            RESTResponse restResponse = new RESTResponse(HttpCode.UNAUTHORIZED, e.getMessage());
            m_logger.info("Returning client error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (DuplicateException e) {
            // 409 Conflict
            RESTResponse restResponse = new RESTResponse(HttpCode.CONFLICT, e.getMessage());
            m_logger.info("Returning client error: {}; request: {}",
                          restResponse.toString(), getFullURI(request));
            RESTService.instance().onRequestRejected(restResponse.getCode().toString());
            sendResponse(response, restResponse);
        } catch (Throwable e) {
            // 500 Internal Error: include a stack trace and report in log.
            m_logger.error("Unexpected exception handling request: " + getFullURI(request), e);
            String stackTrace = Utils.getStackTrace(e);
            RESTResponse restResponse = new RESTResponse(HttpCode.INTERNAL_ERROR, stackTrace);
            RESTService.instance().onRequestFailed(e);
            sendResponse(response, restResponse);
        }
    }    // goGet
    
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    // doPost
    
    /**
     * @see HttpServlet#doPut(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    // doPut
    
    /**
     * @see HttpServlet#doDelete(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }    // doDelete
    
    //----- Private methods

    // Execute the given request and return a RESTResponse or throw an appropriate error.
    private RESTResponse validateAndExecuteRequest(HttpServletRequest request) {
        Map<String, String> variableMap = new HashMap<String, String>();
        String query = extractQueryParam(request, variableMap);
        Tenant tenant = getTenant(variableMap);

        // Command matching expects an encoded URI but without the servlet context, if any.
        String uri = request.getRequestURI();
        String context = request.getContextPath();
        if (!Utils.isEmpty(context)) {
            uri = uri.substring(context.length());
        }
{'fraction_non_alphanumeric': 0.09920442753372535, 'fraction_numerical': 0.005396056727775856, 'mean_word_length': 3.057255122088128, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2771948', 'n_tokens_mistral': 3736, 'n_tokens_neox': 3610, 'n_words': 915}