org.n52.web.ctrl.DataController.java Source code

Java tutorial

Introduction

Here is the source code for org.n52.web.ctrl.DataController.java

Source

/*
 * Copyright (C) 2013-2016 52North Initiative for Geospatial Open Source
 * Software GmbH
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 *
 * If the program is linked with libraries which are licensed under one of
 * the following licenses, the combination of the program with the linked
 * library is not considered a "derivative work" of the program:
 *
 *     - Apache License, version 2.0
 *     - Apache Software License, version 1.0
 *     - GNU Lesser General Public License, version 3
 *     - Mozilla Public License, versions 1.0, 1.1 and 2.0
 *     - Common Development and Distribution License (CDDL), version 1.0
 *
 * Therefore the distribution of the program linked with libraries licensed
 * under the aforementioned licenses, is permitted by the copyright holders
 * if the distribution is compliant with both the GNU General Public License
 * version 2 and the aforementioned licenses.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 */

package org.n52.web.ctrl;

import static org.n52.io.MimeType.APPLICATION_ZIP;
import static org.n52.io.MimeType.TEXT_CSV;
import static org.n52.io.request.IoParameters.createFromQuery;
import static org.n52.io.request.QueryParameters.createFromQuery;
import static org.n52.io.request.RequestSimpleParameterSet.createForSingleSeries;
import static org.n52.io.request.RequestSimpleParameterSet.createFromDesignedParameters;
import static org.n52.web.ctrl.UrlSettings.COLLECTION_DATASETS;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.Interval;
import org.joda.time.Period;
import org.n52.io.DatasetFactoryException;
import org.n52.io.DefaultIoFactory;
import org.n52.io.IntervalWithTimeZone;
import org.n52.io.IoFactory;
import org.n52.io.IoProcessChain;
import org.n52.io.PreRenderingJob;
import org.n52.io.request.IoParameters;
import org.n52.io.request.Parameters;
import org.n52.io.request.RequestParameterSet;
import org.n52.io.request.RequestSimpleParameterSet;
import org.n52.io.request.RequestStyledParameterSet;
import org.n52.io.response.dataset.AbstractValue;
import org.n52.io.response.dataset.Data;
import org.n52.io.response.dataset.DataCollection;
import org.n52.io.response.dataset.DatasetOutput;
import org.n52.io.response.dataset.DatasetType;
import org.n52.io.v1.data.RawFormats;
import org.n52.series.spi.srv.DataService;
import org.n52.series.spi.srv.ParameterService;
import org.n52.series.spi.srv.RawDataService;
import org.n52.web.exception.BadRequestException;
import org.n52.web.exception.InternalServerException;
import org.n52.web.exception.ResourceNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

@RestController
@RequestMapping(value = COLLECTION_DATASETS, produces = { "application/json" })
public class DataController extends BaseController {

    private final static Logger LOGGER = LoggerFactory.getLogger(DataController.class);

    @Autowired
    private DefaultIoFactory<Data<AbstractValue<?>>, DatasetOutput<AbstractValue<?>, ?>, AbstractValue<?>> ioFactoryCreator;

    private DataService<Data<AbstractValue<?>>> dataService;

    private ParameterService<DatasetOutput<AbstractValue<?>, ?>> datasetService;

    private PreRenderingJob preRenderingTask;

    private String requestIntervalRestriction; // optional

    @RequestMapping(value = "/data", produces = { "application/json" }, method = GET)
    public ModelAndView getSeriesData(HttpServletResponse response,
            @RequestParam(required = false) MultiValueMap<String, String> query) throws Exception {
        IoParameters parameters = createFromQuery(query);
        LOGGER.debug("get data with query: {}", parameters);
        return getSeriesCollectionData(response, parameters.toSimpleParameterSet());
    }

    @RequestMapping(value = "/data", produces = { "application/json" }, method = POST)
    public ModelAndView getSeriesCollectionData(HttpServletResponse response,
            @RequestBody RequestSimpleParameterSet parameters) throws Exception {

        LOGGER.debug("get data collection with parameter set: {}", parameters);

        checkForUnknownSeriesIds(parameters, parameters.getDatasets());
        checkAgainstTimespanRestriction(parameters.getTimespan());

        final String datasetType = parameters.getDatasetType();
        IoProcessChain<?> ioChain = createIoFactory(datasetType).withSimpleRequest(parameters).createProcessChain();

        DataCollection<?> processed = ioChain.getData();
        return new ModelAndView().addObject(processed.getAllSeries());
    }

    @RequestMapping(value = "/{seriesId}/data", produces = { "application/json" }, method = GET)
    public ModelAndView getSeriesData(HttpServletResponse response, @PathVariable String seriesId,
            @RequestParam(required = false) MultiValueMap<String, String> query) throws Exception {

        IoParameters map = createFromQuery(query);
        LOGGER.debug("get data for item '{}' with query: {}", seriesId, map);

        IntervalWithTimeZone timespan = map.getTimespan();
        checkAgainstTimespanRestriction(timespan.toString());
        checkForUnknownSeriesIds(map, seriesId);

        RequestSimpleParameterSet parameters = createForSingleSeries(seriesId, map);
        String handleAsDatasetFallback = map.getAsString(Parameters.HANDLE_AS_DATASET_TYPE);
        String datasetType = DatasetType.extractType(seriesId, handleAsDatasetFallback);
        IoProcessChain<?> ioChain = createIoFactory(datasetType).withSimpleRequest(parameters).createProcessChain();

        DataCollection<?> formattedDataCollection = ioChain.getProcessedData();
        final Map<String, ?> processed = formattedDataCollection.getAllSeries();
        return map.isExpanded() ? new ModelAndView().addObject(processed)
                : new ModelAndView().addObject(processed.get(seriesId));
    }

    @RequestMapping(value = "/data", method = POST, params = { RawFormats.RAW_FORMAT })
    public void getRawSeriesCollectionData(HttpServletResponse response,
            @RequestBody RequestSimpleParameterSet parameters) throws Exception {
        checkForUnknownSeriesIds(parameters, parameters.getDatasets());

        LOGGER.debug("get raw data collection with parameters: {}", parameters);
        writeRawData(parameters, response);
    }

    @RequestMapping(value = "/{seriesId}/data", method = GET, params = { RawFormats.RAW_FORMAT })
    public void getRawSeriesData(HttpServletResponse response, @PathVariable String seriesId,
            @RequestParam MultiValueMap<String, String> query) {
        IoParameters map = createFromQuery(query);
        checkForUnknownSeriesIds(map, seriesId);
        LOGGER.debug("getSeriesCollection() with query: {}", map);
        RequestSimpleParameterSet parameters = createForSingleSeries(seriesId, map);
        writeRawData(parameters, response);
    }

    private void writeRawData(RequestSimpleParameterSet parameters, HttpServletResponse response)
            throws InternalServerException, ResourceNotFoundException, BadRequestException {
        if (!dataService.supportsRawData()) {
            throw new BadRequestException(
                    "Querying of raw timeseries data is not supported by the underlying service!");
        }
        final RawDataService rawDataService = dataService.getRawDataService();
        try (final InputStream inputStream = rawDataService.getRawData(parameters)) {
            if (inputStream == null) {
                throw new ResourceNotFoundException("No raw data found.");
            }
            IOUtils.copyLarge(inputStream, response.getOutputStream());
        } catch (IOException e) {
            throw new InternalServerException("Error while querying raw data", e);
        }
    }

    @RequestMapping(value = "/data", produces = { "application/pdf" }, method = POST)
    public void getSeriesCollectionReport(HttpServletResponse response,
            @RequestBody RequestStyledParameterSet parameters) throws Exception {

        IoParameters map = createFromQuery(parameters);
        LOGGER.debug("get data collection report with query: {}", map);

        checkForUnknownSeriesIds(parameters, parameters.getDatasets());
        checkAgainstTimespanRestriction(parameters.getTimespan());

        final String datasetType = parameters.getDatasetType();
        createIoFactory(datasetType).withSimpleRequest(createFromDesignedParameters(parameters))
                .withStyledRequest(parameters).createHandler("application/pdf")
                .writeBinary(response.getOutputStream());

    }

    @RequestMapping(value = "/{seriesId}/data", produces = { "application/pdf" }, method = GET)
    public void getSeriesReport(HttpServletResponse response, @PathVariable String seriesId,
            @RequestParam(required = false) MultiValueMap<String, String> query) throws Exception {

        IoParameters map = createFromQuery(query);
        LOGGER.debug("get data collection report for '{}' with query: {}", seriesId, map);
        RequestSimpleParameterSet parameters = createForSingleSeries(seriesId, map);

        checkAgainstTimespanRestriction(parameters.getTimespan());
        checkForUnknownSeriesIds(map, seriesId);

        final String datasetType = parameters.getDatasetType();
        createIoFactory(datasetType).withSimpleRequest(parameters).createHandler("application/pdf")
                .writeBinary(response.getOutputStream());
    }

    @RequestMapping(value = "/{seriesId}/data", produces = { "application/zip" }, method = GET)
    public void getSeriesAsZippedCsv(HttpServletResponse response, @PathVariable String seriesId,
            @RequestParam(required = false) MultiValueMap<String, String> query) throws Exception {
        IoParameters map = createFromQuery(query);
        LOGGER.debug("get data collection zip for '{}' with query: {}", seriesId, map);
        RequestSimpleParameterSet parameters = createForSingleSeries(seriesId, map);

        checkAgainstTimespanRestriction(parameters.getTimespan());
        checkForUnknownSeriesIds(map, seriesId);

        response.setCharacterEncoding("UTF-8");
        response.setContentType(APPLICATION_ZIP.toString());

        final String datasetType = parameters.getDatasetType();
        createIoFactory(datasetType).withSimpleRequest(parameters).createHandler(APPLICATION_ZIP.toString())
                .writeBinary(response.getOutputStream());
    }

    @RequestMapping(value = "/{seriesId}/data", produces = { "text/csv" }, method = GET)
    public void getSeriesAsCsv(HttpServletResponse response, @PathVariable String seriesId,
            @RequestParam(required = false) MultiValueMap<String, String> query) throws Exception {

        IoParameters map = createFromQuery(query);
        LOGGER.debug("get data collection csv for '{}' with query: {}", seriesId, map);
        RequestSimpleParameterSet parameters = createForSingleSeries(seriesId, map);

        checkAgainstTimespanRestriction(parameters.getTimespan());
        checkForUnknownSeriesIds(map, seriesId);

        response.setCharacterEncoding("UTF-8");
        if (Boolean.parseBoolean(map.getOther("zip"))) {
            response.setContentType(APPLICATION_ZIP.toString());
        } else {
            response.setContentType(TEXT_CSV.toString());
        }

        final String datasetType = parameters.getDatasetType();
        createIoFactory(datasetType).withSimpleRequest(parameters).createHandler(TEXT_CSV.toString())
                .writeBinary(response.getOutputStream());
    }

    @RequestMapping(value = "/data", produces = { "image/png" }, method = POST)
    public void getSeriesCollectionChart(HttpServletResponse response,
            @RequestBody RequestStyledParameterSet parameters) throws Exception {

        IoParameters map = createFromQuery(parameters);
        checkForUnknownSeriesIds(map, parameters.getDatasets());

        LOGGER.debug("get data collection chart with query: {}", map);

        final String datasetType = parameters.getDatasetType();
        createIoFactory(datasetType).withStyledRequest(parameters).createHandler("image/png")
                .writeBinary(response.getOutputStream());
    }

    @RequestMapping(value = "/{seriesId}/data", produces = { "image/png" }, method = GET)
    public void getSeriesChart(HttpServletResponse response, @PathVariable String seriesId,
            @RequestParam(required = false) MultiValueMap<String, String> query) throws Exception {

        IoParameters map = createFromQuery(query);
        LOGGER.debug("get data collection chart for '{}' with query: {}", seriesId, map);
        checkAgainstTimespanRestriction(map.getTimespan().toString());
        checkForUnknownSeriesIds(map, seriesId);

        String handleAsDatasetFallback = map.getAsString(Parameters.HANDLE_AS_DATASET_TYPE);
        String observationType = DatasetType.extractType(seriesId, handleAsDatasetFallback);
        RequestSimpleParameterSet parameters = map.toSimpleParameterSet();
        createIoFactory(observationType).withSimpleRequest(parameters).createHandler("image/png")
                .writeBinary(response.getOutputStream());
    }

    @RequestMapping(value = "/{seriesId}/{chartQualifier}", produces = { "image/png" }, method = GET)
    public void getSeriesChartByInterval(HttpServletResponse response, @PathVariable String seriesId,
            @PathVariable String chartQualifier,
            @RequestParam(required = false) MultiValueMap<String, String> query) throws Exception {
        if (preRenderingTask == null) {
            throw new ResourceNotFoundException("Diagram prerendering is not enabled.");
        }
        if (!preRenderingTask.hasPrerenderedImage(seriesId, chartQualifier)) {
            throw new ResourceNotFoundException("No pre-rendered chart found for timeseries '" + seriesId + "'.");
        }

        LOGGER.debug("get prerendered chart for '{}' ({})", seriesId, chartQualifier);
        preRenderingTask.writePrerenderedGraphToOutputStream(seriesId, chartQualifier, response.getOutputStream());
    }

    private void checkAgainstTimespanRestriction(String timespan) {
        if (requestIntervalRestriction != null) {
            Duration duration = Period.parse(requestIntervalRestriction).toDurationFrom(new DateTime());
            if (duration.getMillis() < Interval.parse(timespan).toDurationMillis()) {
                throw new BadRequestException("Requested timespan is to long, please use a period shorter than '"
                        + requestIntervalRestriction + "'");
            }
        }
    }

    private void checkForUnknownSeriesIds(RequestParameterSet parameters, String... seriesIds) {
        checkForUnknownSeriesIds(IoParameters.createFromQuery(parameters), seriesIds);
    }

    private void checkForUnknownSeriesIds(IoParameters parameters, String... seriesIds) {
        if (seriesIds != null) {
            for (String id : seriesIds) {
                if (!datasetService.exists(id, parameters)) {
                    throw new ResourceNotFoundException("The series with id '" + id + "' was not found.");
                }
            }
        }
    }

    private IoFactory<Data<AbstractValue<?>>, DatasetOutput<AbstractValue<?>, ?>, AbstractValue<?>> createIoFactory(
            final String datasetType) throws DatasetFactoryException {
        if (!ioFactoryCreator.isKnown(datasetType)) {
            throw new ResourceNotFoundException("unknown dataset type: " + datasetType);
        }
        return ioFactoryCreator.create(datasetType)
                //                .withBasePath(getRootResource())
                .withDataService(dataService).withDatasetService(datasetService);
    }

    private URI getRootResource() throws URISyntaxException, MalformedURLException {
        return getServletConfig().getServletContext().getResource("/").toURI();
    }

    // TODO set preredering config instead of task

    public PreRenderingJob getPreRenderingTask() {
        return preRenderingTask;
    }

    public void setPreRenderingTask(PreRenderingJob prerenderingTask) {
        this.preRenderingTask = prerenderingTask;
    }

    public String getRequestIntervalRestriction() {
        return requestIntervalRestriction;
    }

    public void setRequestIntervalRestriction(String requestIntervalRestriction) {
        // validate requestIntervalRestriction, if it's no period an exception occured
        Period.parse(requestIntervalRestriction);
        LOGGER.debug("CONFIG: request.interval.restriction={}", requestIntervalRestriction);
        this.requestIntervalRestriction = requestIntervalRestriction;
    }

    public DataService<Data<AbstractValue<?>>> getDataService() {
        return dataService;
    }

    public void setDataService(DataService<Data<AbstractValue<?>>> dataService) {
        this.dataService = dataService;
    }

    public ParameterService<DatasetOutput<AbstractValue<?>, ?>> getDatasetService() {
        return datasetService;
    }

    public void setDatasetService(ParameterService<DatasetOutput<AbstractValue<?>, ?>> datasetService) {
        this.datasetService = datasetService;
    }

}