Package org.apache.shindig.protocol

Source Code of org.apache.shindig.protocol.DataServiceServlet

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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 org.apache.shindig.protocol;

import org.apache.commons.lang3.StringUtils;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.Nullable;
import org.apache.shindig.common.servlet.HttpUtil;
import org.apache.shindig.protocol.conversion.BeanConverter;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Servlet used to process REST requests (/rest/* etc.)
*/
public class DataServiceServlet extends ApiServlet {

  private static final Logger LOG = Logger.getLogger(DataServiceServlet.class.getName());

  public static final Set<String> ALLOWED_CONTENT_TYPES =
      new ImmutableSet.Builder<String>().addAll(ContentTypes.ALLOWED_JSON_CONTENT_TYPES)
          .addAll(ContentTypes.ALLOWED_XML_CONTENT_TYPES)
          .addAll(ContentTypes.ALLOWED_ATOM_CONTENT_TYPES).build();

  protected static final String X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override";

  @Override
  protected void doGet(HttpServletRequest servletRequest,
      HttpServletResponse servletResponse)
      throws ServletException, IOException {
    executeRequest(servletRequest, servletResponse);
  }

  @Override
  protected void doPut(HttpServletRequest servletRequest,
      HttpServletResponse servletResponse)
      throws ServletException, IOException {
    try {
      ContentTypes.checkContentTypes(ALLOWED_CONTENT_TYPES, servletRequest.getContentType());
      executeRequest(servletRequest, servletResponse);
    } catch (ContentTypes.InvalidContentTypeException icte) {
      sendError(servletResponse,
          new ResponseItem(HttpServletResponse.SC_BAD_REQUEST, icte.getMessage()));
    }
  }

  @Override
  protected void doDelete(HttpServletRequest servletRequest,
      HttpServletResponse servletResponse)
      throws ServletException, IOException {
    executeRequest(servletRequest, servletResponse);
  }

  @Override
  protected void doPost(HttpServletRequest servletRequest,
      HttpServletResponse servletResponse)
      throws ServletException, IOException {
    try {
      ContentTypes.checkContentTypes(ALLOWED_CONTENT_TYPES, servletRequest.getContentType());
      executeRequest(servletRequest, servletResponse);
    } catch (ContentTypes.InvalidContentTypeException icte) {
      sendError(servletResponse,
          new ResponseItem(HttpServletResponse.SC_BAD_REQUEST, icte.getMessage()));
    }
  }

  /**
   * Actual dispatch handling for servlet requests
   */
  void executeRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
      throws IOException {
    if (LOG.isLoggable(Level.FINEST)) {
      LOG.finest("Handling restful request for " + servletRequest.getPathInfo());
    }

    setCharacterEncodings(servletRequest, servletResponse);

    SecurityToken token = getSecurityToken(servletRequest);
    if (token == null) {
      sendSecurityError(servletResponse);
      return;
    }

    HttpUtil.setCORSheader(servletResponse, containerConfig.<String>getList(token.getContainer(),
        "gadgets.parentOrigins"));

    handleSingleRequest(servletRequest, servletResponse, token);
  }

  @Override
  protected void sendError(HttpServletResponse servletResponse, ResponseItem responseItem)
      throws IOException {
    String errorMessage = responseItem.getErrorMessage();
    int errorCode = responseItem.getErrorCode();
    if (errorCode < 0) {
      // Map JSON-RPC error codes into HTTP error codes as best we can
      // TODO: Augment the error message (if missing) with a default
      switch (errorCode) {
        case -32700:
        case -32602:
        case -32600:
          // Parse error, invalid params, and invalid request
          errorCode = HttpServletResponse.SC_BAD_REQUEST;
          break;
        case -32601:
          // Procedure doesn't exist
          errorCode = HttpServletResponse.SC_NOT_IMPLEMENTED;
          break;
        case -32603:
        default:
          // Internal server error, or any application-defined error
          errorCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
          break;
      }
    }

    servletResponse.sendError(errorCode, errorMessage);
  }

  /**
   * Handler for non-batch requests.
   */
  private void handleSingleRequest(HttpServletRequest servletRequest,
      HttpServletResponse servletResponse, SecurityToken token) throws IOException {

    // Always returns a non-null handler.
    RestHandler handler = getRestHandler(servletRequest);

    // Get Content-Type and format
    String format = null;
    String contentType = null;

    try {
      format = servletRequest.getParameter(FORMAT_PARAM);
    } catch (Throwable t) {
      // this happens while testing
      if (LOG.isLoggable(Level.FINE)) {
        LOG.fine("Unexpected error : format param is null " + t.toString());
      }
    }
    try {
      // TODO: First implementation causes bug when Content-Type is application/atom+xml. Fix is applied.
      contentType = ContentTypes.extractMimePart(servletRequest.getContentType());
    } catch (Throwable t) {
      //this happens while testing
      if (LOG.isLoggable(Level.FINE)) {
        LOG.fine("Unexpected error : content type is null " + t.toString());
      }
    }

    // Get BeanConverter for Request payload.
    BeanConverter requestConverter = getConverterForRequest(contentType, format);

    // Get BeanConverter for Response body.
    BeanConverter responseConverter = getConverterForFormat(format);

    Reader bodyReader = null;
    if (!servletRequest.getMethod().equals("GET") && !servletRequest.getMethod().equals("HEAD")) {
      bodyReader = servletRequest.getReader();
    }

    // Execute the request
    @SuppressWarnings("unchecked")
    Map<String, String[]> parameterMap = servletRequest.getParameterMap();
    Future<?> future = handler.execute(parameterMap, bodyReader, token, requestConverter);
    ResponseItem responseItem = getResponseItem(future);

    servletResponse.setContentType(responseConverter.getContentType());
    if (responseItem.getErrorCode() >= 200 && responseItem.getErrorCode() < 400) {
      PrintWriter writer = servletResponse.getWriter();
      Object response = responseItem.getResponse();
      // TODO: ugliness resulting from not using RestfulItem
      if (!(response instanceof DataCollection) && !(response instanceof RestfulCollection)) {
        response = ImmutableMap.of("entry", response);
      }

      // JSONP style callbacks
      String callback = (this.isJSONPAllowed && HttpUtil.isJSONP(servletRequest) &&
          ContentTypes.OUTPUT_JSON_CONTENT_TYPE.equals(responseConverter.getContentType())) ?
          servletRequest.getParameter("callback") : null;

      if (callback != null) writer.write(callback + '(');
      writer.write(responseConverter.convertToString(response));
      if (callback != null) writer.write(");\n");
    } else {
      sendError(servletResponse, responseItem);
    }
  }

  protected RestHandler getRestHandler(HttpServletRequest servletRequest) {
    // TODO Rework to allow sub-services
    String path = servletRequest.getPathInfo();

    // TODO - This shouldnt be on BaseRequestItem
    String method = servletRequest.getParameter(X_HTTP_METHOD_OVERRIDE);
    if (method == null) {
      method = servletRequest.getMethod();
    }

    // Always returns a non-null handler.
    return dispatcher.getRestHandler(path, method.toUpperCase());
  }

  /*
   * Return the right BeanConverter to convert the request payload.
   */
  public BeanConverter getConverterForRequest(@Nullable String contentType, String format) {
    if (StringUtils.isNotBlank(contentType)) {
      return getConverterForContentType(contentType);
    } else {
      return getConverterForFormat(format);
    }
  }

  /**
   * Return BeanConverter based on content type.
   * @param contentType the content type for the converter.
   * @return BeanConverter based on the contentType input param. Will default to JSON
   */
  protected BeanConverter getConverterForContentType(String contentType) {
    return ContentTypes.ALLOWED_ATOM_CONTENT_TYPES.contains(contentType) ? atomConverter :
        ContentTypes.ALLOWED_XML_CONTENT_TYPES.contains(contentType) ? xmlConverter : jsonConverter;
  }

  /**
   * Return BeanConverter based on format request parameter.
   * @param format the format for the converter.
   * @return BeanConverter based on the format input param. Will default to JSON
   */
  protected BeanConverter getConverterForFormat(String format) {
    return ATOM_FORMAT.equals(format) ? atomConverter : XML_FORMAT.equals(format) ? xmlConverter :
        jsonConverter;
  }
}
TOP

Related Classes of org.apache.shindig.protocol.DataServiceServlet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.