org.killbill.billing.plugin.analytics.http.ReportsServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.killbill.billing.plugin.analytics.http.ReportsServlet.java

Source

/*
 * Copyright 2010-2014 Ning, Inc.
 * Copyright 2014-2015 Groupon, Inc
 * Copyright 2014-2015 The Billing Project, LLC
 *
 * The Billing Project 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.killbill.billing.plugin.analytics.http;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.sql.SQLException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.killbill.billing.plugin.analytics.api.user.AnalyticsUserApi;
import org.killbill.billing.plugin.analytics.json.CSVNamedXYTimeSeries;
import org.killbill.billing.plugin.analytics.json.Chart;
import org.killbill.billing.plugin.analytics.json.DataMarker;
import org.killbill.billing.plugin.analytics.json.NamedXYTimeSeries;
import org.killbill.billing.plugin.analytics.json.ReportConfigurationJson;
import org.killbill.billing.plugin.analytics.json.XY;
import org.killbill.billing.plugin.analytics.reports.ReportsUserApi;
import org.killbill.billing.plugin.analytics.reports.analysis.Smoother;
import org.killbill.billing.plugin.analytics.reports.analysis.Smoother.SmootherType;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.osgi.service.log.LogService;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.base.Strings;

// Handle /plugins/killbill-analytics/reports/<reportName>
public class ReportsServlet extends BaseServlet {

    private static final String CSV_DATA_FORMAT = "csv";
    private static final String JSON_DATA_FORMAT = "json";

    @VisibleForTesting
    static final DateTimeFormatter DATE_FORMAT = DateTimeFormat.forPattern("yyyy-MM-dd");

    private static final String REPORTS_QUERY_NAME = "name";
    private static final String REPORTS_QUERY_START_DATE = "startDate";
    private static final String REPORTS_QUERY_END_DATE = "endDate";
    private static final String REPORTS_SMOOTHER_NAME = "smooth";
    private static final String REPORTS_DATA_FORMAT = "format";
    private static final String REPORT_QUERY_SQL_ONLY = "sqlOnly";

    public ReportsServlet(final AnalyticsUserApi analyticsUserApi, final ReportsUserApi reportsUserApi,
            final LogService logService) {
        super(analyticsUserApi, reportsUserApi, logService);
    }

    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
            throws ServletException, IOException {
        final TenantContext context = createCallContext(req, resp);

        final String reportName = (String) req.getAttribute(REPORT_NAME_ATTRIBUTE);
        if (reportName != null) {
            final ReportConfigurationJson reportConfigurationJson;
            try {
                reportConfigurationJson = reportsUserApi.getReportConfiguration(reportName, context);
            } catch (SQLException e) {
                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage());
                return;
            }

            if (reportConfigurationJson == null) {
                resp.sendError(404, reportName + "");
            }
            resp.getOutputStream().write(jsonMapper.writeValueAsBytes(reportConfigurationJson));
            resp.setContentType("application/json");
        } else {
            doHandleReports(req, resp, context);
        }
    }

    @Override
    protected void doPost(final HttpServletRequest req, final HttpServletResponse resp)
            throws ServletException, IOException {
        final CallContext context = createCallContext(req, resp);

        final ReportConfigurationJson reportConfigurationJson = jsonMapper.readValue(req.getInputStream(),
                ReportConfigurationJson.class);

        final ReportConfigurationJson existingReportConfiguration;
        try {
            existingReportConfiguration = reportsUserApi
                    .getReportConfiguration(reportConfigurationJson.getReportName(), context);
        } catch (SQLException e) {
            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage());
            return;
        }

        if (existingReportConfiguration == null) {
            reportsUserApi.createReport(reportConfigurationJson, context);
            resp.setStatus(201);
        } else {
            resp.setStatus(409);
        }

        resp.setHeader("Location",
                "/plugins/killbill-analytics/reports/" + reportConfigurationJson.getReportName());
    }

    @Override
    protected void doPut(final HttpServletRequest req, final HttpServletResponse resp)
            throws ServletException, IOException {
        final CallContext context = createCallContext(req, resp);

        final String reportName = (String) req.getAttribute(REPORT_NAME_ATTRIBUTE);
        if (reportName == null) {
            return;
        }

        if ((Boolean) req.getAttribute(SHOULD_REFRESH)) {
            reportsUserApi.refreshReport(reportName, context);
        } else {
            final ReportConfigurationJson reportConfigurationJson = jsonMapper.readValue(req.getInputStream(),
                    ReportConfigurationJson.class);
            reportsUserApi.updateReport(reportName, reportConfigurationJson, context);
        }
    }

    @Override
    protected void doDelete(final HttpServletRequest req, final HttpServletResponse resp)
            throws ServletException, IOException {
        final CallContext context = createCallContext(req, resp);

        final String reportName = (String) req.getAttribute(REPORT_NAME_ATTRIBUTE);
        if (reportName == null) {
            reportsUserApi.clearCaches(context);
        } else {
            reportsUserApi.deleteReport(reportName, context);
        }
    }

    private void doHandleReports(final HttpServletRequest req, final HttpServletResponse resp,
            final TenantContext context) throws ServletException, IOException {
        final String[] rawReportNames = req.getParameterValues(REPORTS_QUERY_NAME);
        if (rawReportNames == null || rawReportNames.length == 0) {
            listReports(req, resp, context);
            return;
        }

        final LocalDate startDate = Strings.emptyToNull(req.getParameter(REPORTS_QUERY_START_DATE)) != null
                ? DATE_FORMAT.parseLocalDate(req.getParameter(REPORTS_QUERY_START_DATE))
                : null;
        final LocalDate endDate = Strings.emptyToNull(req.getParameter(REPORTS_QUERY_END_DATE)) != null
                ? DATE_FORMAT.parseLocalDate(req.getParameter(REPORTS_QUERY_END_DATE))
                : null;

        final boolean sqlOnly = req.getParameter(REPORT_QUERY_SQL_ONLY) != null;

        if (sqlOnly) {
            for (final String sql : reportsUserApi.getSQLForReport(rawReportNames, startDate, endDate, context)) {
                resp.getOutputStream().write(("\n" + sql + "\n").getBytes(Charset.forName("UTF-8")));
            }
            resp.setContentType("text/plain");
        } else {
            final SmootherType smootherType = Smoother
                    .fromString(Strings.emptyToNull(req.getParameter(REPORTS_SMOOTHER_NAME)));

            // TODO PIERRE Switch to an equivalent of StreamingOutputStream?
            final List<Chart> results = reportsUserApi.getDataForReport(rawReportNames, startDate, endDate,
                    smootherType, context);

            final String format = Objects.firstNonNull(Strings.emptyToNull(req.getParameter(REPORTS_DATA_FORMAT)),
                    JSON_DATA_FORMAT);
            if (CSV_DATA_FORMAT.equals(format)) {
                final OutputStream out = resp.getOutputStream();
                writeAsCSV(results, out);
                resp.setContentType("text/csv");
            } else {
                resp.getOutputStream().write(jsonMapper.writeValueAsBytes(results));
                resp.setContentType("application/json");
            }
        }
    }

    private void listReports(final HttpServletRequest req, final HttpServletResponse resp,
            final TenantContext context) throws ServletException, IOException {
        final List<ReportConfigurationJson> reports = reportsUserApi.getReports(context);
        resp.getOutputStream().write(jsonMapper.writeValueAsBytes(reports));
        resp.setContentType("application/json");
    }

    static void writeAsCSV(final List<Chart> charts, final OutputStream out) throws IOException {
        for (Chart cur : charts) {
            switch (cur.getType()) {
            case TIMELINE:
                for (final DataMarker marker : cur.getData()) {
                    final NamedXYTimeSeries namedXYTimeSeries = (NamedXYTimeSeries) marker;
                    for (final XY value : namedXYTimeSeries.getValues()) {
                        out.write(csvMapper
                                .writeValueAsBytes(new CSVNamedXYTimeSeries(namedXYTimeSeries.getName(), value)));
                    }
                }
                break;
            case COUNTERS:
                break;
            }
        }
    }
}