org.apache.shindig.social.opensocial.service.JsonRpcServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.shindig.social.opensocial.service.JsonRpcServlet.java

Source

/*
 * 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.social.opensocial.service;

import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.util.JsonConversionUtil;
import org.apache.shindig.social.ResponseError;
import org.apache.shindig.social.opensocial.spi.DataCollection;
import org.apache.shindig.social.opensocial.spi.RestfulCollection;

import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.Future;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * JSON-RPC handler servlet.
 */
public class JsonRpcServlet extends ApiServlet {

    @Override
    protected void doGet(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
            throws ServletException, IOException {
        SecurityToken token = getSecurityToken(servletRequest);
        if (token == null) {
            sendSecurityError(servletResponse);
            return;
        }

        try {
            setCharacterEncodings(servletRequest, servletResponse);
            JSONObject request = JsonConversionUtil.fromRequest(servletRequest);
            dispatch(request, servletRequest, servletResponse, token);
        } catch (JSONException je) {
            // FIXME
        }
    }

    @Override
    protected void doPost(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
            throws ServletException, IOException {
        SecurityToken token = getSecurityToken(servletRequest);
        if (token == null) {
            sendSecurityError(servletResponse);
            return;
        }

        setCharacterEncodings(servletRequest, servletResponse);
        servletResponse.setContentType("application/json");

        try {
            String content = IOUtils.toString(servletRequest.getInputStream(),
                    servletRequest.getCharacterEncoding());
            if ((content.indexOf('[') != -1) && content.indexOf('[') < content.indexOf('{')) {
                // Is a batch
                JSONArray batch = new JSONArray(content);
                dispatchBatch(batch, servletRequest, servletResponse, token);
            } else {
                JSONObject request = new JSONObject(content);
                dispatch(request, servletRequest, servletResponse, token);
            }
        } catch (JSONException je) {
            sendBadRequest(je, servletResponse);
        }
    }

    protected void dispatchBatch(JSONArray batch, HttpServletRequest servletRequest,
            HttpServletResponse servletResponse, SecurityToken token) throws JSONException, IOException {
        // Use linked hash map to preserve order
        List<Future<?>> responses = Lists.newArrayListWithExpectedSize(batch.length());

        // Gather all Futures.  We do this up front so that
        // the first call to get() comes after all futures are created,
        // which allows for implementations that batch multiple Futures
        // into single requests.
        for (int i = 0; i < batch.length(); i++) {
            JSONObject batchObj = batch.getJSONObject(i);
            RpcRequestItem requestItem = new RpcRequestItem(batchObj, token, jsonConverter);
            responses.add(handleRequestItem(requestItem, servletRequest));
        }

        // Resolve each Future into a response.
        // TODO: should use shared deadline across each request
        JSONArray result = new JSONArray();
        for (int i = 0; i < batch.length(); i++) {
            JSONObject batchObj = batch.getJSONObject(i);
            String key = null;
            if (batchObj.has("id")) {
                key = batchObj.getString("id");
            }
            result.put(getJSONResponse(key, getResponseItem(responses.get(i))));
        }
        servletResponse.getWriter().write(result.toString());
    }

    protected void dispatch(JSONObject request, HttpServletRequest servletRequest,
            HttpServletResponse servletResponse, SecurityToken token) throws JSONException, IOException {
        String key = null;
        if (request.has("id")) {
            key = request.getString("id");
        }
        RpcRequestItem requestItem = new RpcRequestItem(request, token, jsonConverter);

        // Resolve each Future into a response.
        // TODO: should use shared deadline across each request
        ResponseItem response = getResponseItem(handleRequestItem(requestItem, servletRequest));
        JSONObject result = getJSONResponse(key, response);
        servletResponse.getWriter().write(result.toString());
    }

    private JSONObject getJSONResponse(String key, ResponseItem responseItem) throws JSONException {
        JSONObject result = new JSONObject();
        if (key != null) {
            result.put("id", key);
        }
        if (responseItem.getError() != null) {
            result.put("error", getErrorJson(responseItem));
        } else {
            Object response = responseItem.getResponse();
            JSONObject converted = (JSONObject) jsonConverter.convertToJson(response);

            if (response instanceof RestfulCollection) {
                // FIXME this is a little hacky because of the field names in the RestfulCollection
                converted.put("list", converted.remove("entry"));
                result.put("data", converted);
            } else if (response instanceof DataCollection) {
                if (converted.has("entry")) {
                    result.put("data", converted.get("entry"));
                }
            } else {
                result.put("data", converted);
            }
        }
        return result;
    }

    // TODO(doll): Refactor the responseItem so that the fields on it line up with this format.
    // Then we can use the general converter to output the response to the client and we won't
    // be harcoded to json.
    private JSONObject getErrorJson(ResponseItem responseItem) throws JSONException {
        JSONObject error = new JSONObject();
        error.put("code", responseItem.getError().getHttpErrorCode());

        String message = responseItem.getError().toString();
        if (StringUtils.isNotBlank(responseItem.getErrorMessage())) {
            message += ": " + responseItem.getErrorMessage();
        }
        error.put("message", message);
        return error;
    }

    @Override
    protected void sendError(HttpServletResponse servletResponse, ResponseItem responseItem) throws IOException {
        try {
            JSONObject error = getErrorJson(responseItem);
            servletResponse.getWriter().write(error.toString());
        } catch (JSONException je) {
            // This really shouldn't ever happen
            servletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    "Error generating error response " + je.getMessage());
        }
    }

    private void sendBadRequest(Throwable t, HttpServletResponse response) throws IOException {
        sendError(response, new ResponseItem(ResponseError.BAD_REQUEST, "Invalid batch - " + t.getMessage()));
    }
}