com.github.dbourdette.otto.web.controller.api.ApiController.java Source code

Java tutorial

Introduction

Here is the source code for com.github.dbourdette.otto.web.controller.api.ApiController.java

Source

/*
 * Copyright 2011 Damien Bourdette
 *
 * 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.github.dbourdette.otto.web.controller.api;

import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.github.dbourdette.otto.source.IntervalEventsQuery;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.github.dbourdette.otto.security.Security;
import com.github.dbourdette.otto.security.UnauthorizedException;
import com.github.dbourdette.otto.source.Source;
import com.github.dbourdette.otto.util.Page;
import com.github.dbourdette.otto.web.service.RemoteEventsFacade;
import com.mongodb.DBObject;

/**
 * Controller for external apis.
 */
@Controller
public class ApiController {

    private static final int JSONAPI_PAGE_SIZE = 1000;

    private static final String FILTER_QUERY_PREFIX = "f_";

    private static final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTimeNoMillis();

    @Inject
    private RemoteEventsFacade remoteEventsFacade;

    private static final byte[] EMPTY_GIF;

    static {
        try {
            EMPTY_GIF = IOUtils.toByteArray(ApiController.class.getResourceAsStream("empty.gif"));
        } catch (IOException e) {
            throw new RuntimeException("Failed to load empty.gif");
        }
    }

    @RequestMapping(value = "/api/sources/{name}/events", method = RequestMethod.POST)
    public void post(@PathVariable String name, HttpServletRequest request, HttpServletResponse response) {
        remoteEventsFacade.post(name, copyParams(request));

        response.setStatus(200);
    }

    @RequestMapping(value = "/jsonapi/sources/{name}/events", method = RequestMethod.GET, headers = "Accept=application/json")
    public void eventsJson(@PathVariable String name, @RequestParam(required = false) Integer page,
            @RequestParam(required = false) String from, @RequestParam(required = false) String to,
            HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (!Security.hasSource(name)) {
            throw new UnauthorizedException("You don't have access to this source");
        }

        DateTime fromDate = parseDateTime(from), toDate = parseDateTime(to);

        if (fromDate != null && fromDate.isAfter(new DateTime())) {
            throw new BadRequestException("from Date must be in past");
        }

        if (fromDate != null && toDate != null && fromDate.isAfter(toDate)) {
            throw new BadRequestException("from Date must be before to Date");
        }

        IntervalEventsQuery query = new IntervalEventsQuery(fromDate, toDate);
        query.readFilters(request, FILTER_QUERY_PREFIX);
        query.setPage(page);
        query.setPageSize(JSONAPI_PAGE_SIZE);

        Source source = Source.findByName(name);
        Page<DBObject> events = source.findEvents(query);

        ObjectMapper mapper = new ObjectMapper();
        mapper.getSerializationConfig().set(SerializationConfig.Feature.INDENT_OUTPUT, true);

        ObjectNode root = mapper.createObjectNode();

        root.put("count", events.getTotalCount());
        root.put("page", Page.fixPage(page));
        root.put("pageCount", events.getPageCount());

        ArrayNode eventsNode = root.putArray("events");

        for (DBObject event : events.getItems()) {
            ObjectNode eventNode = mapper.createObjectNode();

            for (String key : event.keySet()) {
                if (!"_id".equals(key)) {
                    eventNode.put(key, formatValue(event.get(key)));
                }
            }

            eventsNode.add(eventNode);
        }

        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        mapper.writeValue(response.getOutputStream(), root);
    }

    @RequestMapping(value = "/imgapi/sources/{name}/event.gif")
    public void gifPost(@PathVariable String name, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        remoteEventsFacade.post(name, copyParams(request));

        response.setHeader("Cache-Control", "private, no-cache");
        response.setHeader("Content-Type", "image/gif");
        response.setHeader("Pragma", "no-cache");
        response.setStatus(200);
        response.getOutputStream().write(EMPTY_GIF);
    }

    /**
     * Since we are using @Async in our RemoteEventsFacade we need to deep copy map of parameters from request.
     * At least tomcat reuse request objects and map would be empty when @Async code is invoked.
     */
    private Map<String, String> copyParams(HttpServletRequest request) {
        Map<String, String> params = new HashMap<String, String>();

        Enumeration<String> names = request.getParameterNames();

        while (names.hasMoreElements()) {
            String name = names.nextElement();

            params.put(name, request.getParameter(name));
        }

        return params;
    }

    private String formatValue(Object value) {
        if (value instanceof Date) {
            return DATE_TIME_FORMATTER.print(new DateTime(value));
        } else {
            return value == null ? null : value.toString();
        }
    }

    private DateTime parseDateTime(String value) {
        if (StringUtils.isEmpty(value)) {
            return null;
        } else {
            try {
                return DATE_TIME_FORMATTER.parseDateTime(value);
            } catch (Exception e) {
                throw new BadRequestException("Failed to parse date: " + value);
            }
        }
    }

}