Package com.google.visualization.datasource

Source Code of com.google.visualization.datasource.DataSourceService

// Copyright 2009 Google Inc.
//
// 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.google.visualization.datasource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Set;

import com.google.visualization.datasource.base.DataSourceException;
import com.google.visualization.datasource.base.InvalidQueryException;
import com.google.visualization.datasource.base.MessagesEnum;
import com.google.visualization.datasource.base.OutputType;
import com.google.visualization.datasource.base.ReasonType;
import com.google.visualization.datasource.base.ResponseStatus;
import com.google.visualization.datasource.datatable.DataTable;
import com.google.visualization.datasource.query.AggregationColumn;
import com.google.visualization.datasource.query.Query;
import com.google.visualization.datasource.query.ScalarFunctionColumn;
import com.google.visualization.datasource.query.engine.QueryEngine;
import com.google.visualization.datasource.render.Renderer;
import com.google.visualization.datasource.render.RendererFactory;
import com.ibm.icu.util.ULocale;

/**
* A Helper class providing convenience functions for serving data source requests.
* <p/>
* The class enables replying to a data source request with a single method which encompasses
* all the request processing - <code>executeDataSourceServletFlow</code>.
* To enable users to change the default flow all the basic operations (such as: query parsing,
* data table creation, query execution, and response creation) are also exposed.
*
* @author Yaniv S.
*/
public class DataSourceService {
    private static final Log log = LogFactory.getLog(DataSourceService.class);

    /**
     * Executes the default data source servlet flow.
     *
     * @param request                The HttpServletRequest.
     * @param response               The HttpServletResponse.
     * @param generator              An implementation of {@link com.google.visualization.datasource.DataTableGenerator} interface.
     * @param isRestrictedAccessMode Indicates whether the server should serve trusted domains only.
     *                               Currently this translates to serving only requests from the same domain.
     * @throws IOException         In case of I/O errors.
     * @throws DataSourceException Invalid parameters
     */
    public void execute(HttpServletRequest request, HttpServletResponse response,
                        DataTableGenerator generator,
                        boolean isRestrictedAccessMode)
            throws IOException, DataSourceException {
        // Extract the data source request parameters.
        this.execute(request, response, new DataSourceRequest(request), generator, isRestrictedAccessMode);
    }

    /**
     * Executes the default data source servlet flow. Create renderer based on data source parameters.
     *
     * @param request                The HttpServletRequest.
     * @param response               The HttpServletResponse.
     * @param dataSourceRequest      Request parameters
     * @param generator              An implementation of {@link com.google.visualization.datasource.DataTableGenerator} interface.
     * @param isRestrictedAccessMode Indicates whether the server should serve trusted domains only.
     *                               Currently this translates to serving only requests from the same domain.
     * @throws IOException In case of I/O errors.
     */
    public void execute(HttpServletRequest request, HttpServletResponse response,
                        DataSourceRequest dataSourceRequest,
                        DataTableGenerator generator,
                        boolean isRestrictedAccessMode)
            throws IOException {
        final Renderer renderer = RendererFactory.create(dataSourceRequest.getDataSourceParameters().getOutputType());
        this.execute(request, response, dataSourceRequest, generator, renderer, isRestrictedAccessMode);
    }

    /**
     * Executes the default data source servlet flow.
     * <p/>
     * The default flow is as follows:
     * - Parse the request parameters.
     * - Verify access is approved (for restricted access mode only).
     * - Split the query.
     * - Generate the data-table using the data-table generator.
     * - Run the completion query.
     * - Set the servlet response.
     * <p/>
     * Usage note : this function executes the same flow provided to Servlets that inherit
     * <code>DataSourceServlet</code>.
     * Use this function when the default flow is required but <code>DataSourceServlet</code>
     * cannot be inherited (e.g., your servlet already inherits from anther class, or not in a
     * servlet context).
     *
     * @param request                The HttpServletRequest.
     * @param response               The HttpServletResponse.
     * @param dataSourceRequest      Request parameters
     * @param generator              An implementation of {@link DataTableGenerator} interface.
     * @param renderer               Output format
     * @param isRestrictedAccessMode Indicates whether the server should serve trusted domains only.
     *                               Currently this translates to serving only requests from the same domain.
     * @throws IOException In case of I/O errors.
     */
    public void execute(HttpServletRequest request, HttpServletResponse response,
                        DataSourceRequest dataSourceRequest,
                        DataTableGenerator generator,
                        Renderer renderer,
                        boolean isRestrictedAccessMode)
            throws IOException {
        try {
            if(isRestrictedAccessMode) {
                // Verify that the request is approved for access.
                this.verifyAccessApproved(dataSourceRequest);
            }

            // Split the query.
            QueryPair query = this.splitQuery(dataSourceRequest.getQuery(),
                    generator.getCapabilities());

            // Generate the data table.
            DataTable dataTable = generator.create(query.getDataSourceQuery(), request);

            // Apply the completion query to the data table.
            DataTable newDataTable = this.applyQuery(query.getCompletionQuery(), dataTable,
                    dataSourceRequest.getUserLocale());

            // Set the response.
            CharSequence output = renderer.render(dataSourceRequest, newDataTable);

            renderer.setHeaders(dataSourceRequest, response);

            OutputStream outputStream = response.getOutputStream();
            outputStream.write(output.toString().getBytes(renderer.getCharset()));
        }
        catch(DataSourceException e) {
            OutputStream outputStream = response.getOutputStream();
            outputStream.write(renderer.error(dataSourceRequest,
                    new ResponseStatus(e)).toString().getBytes(renderer.getCharset()));
        }
    }

    /**
     * Checks that the given request is sent from the same domain as that of the server.
     *
     * @param req The data source request.
     * @throws DataSourceException If the access for this request is denied.
     */
    protected void verifyAccessApproved(DataSourceRequest req) throws DataSourceException {
        // The library requires the request to be same origin for JSON and JSONP.
        // Check for (!csv && !html && !tsv-excel) to make sure any output type
        // added in the future will be restricted to the same domain by default.
        OutputType outType = req.getDataSourceParameters().getOutputType();
        if(outType != OutputType.CSV && outType != OutputType.TSV_EXCEL
                && outType != OutputType.HTML && !req.isSameOrigin()) {
            throw new DataSourceException(ReasonType.ACCESS_DENIED,
                    "Unauthorized request. Cross domain requests are not supported.");
        }
    }

    // -------------------------- Query helper methods ----------------------------------------------

    /**
     * Applies the given <code>Query</code> on the given <code>DataTable</code> and returns the
     * resulting <code>DataTable</code>. This method may change the given DataTable.
     * Error messages produced by this method will be localized according to the passed locale
     * unless the specified {@code DataTable} has a non null locale.
     *
     * @param query     The query object.
     * @param dataTable The data table on which to apply the query.
     * @param locale    The user locale for the current request.
     * @return The data table result of the query execution over the given data table.
     * @throws InvalidQueryException If the query is invalid.
     * @throws DataSourceException   If the data source cannot execute the query.
     */
    protected DataTable applyQuery(Query query, DataTable dataTable, ULocale locale)
            throws DataSourceException {
        dataTable.setLocaleForUserMessages(locale);
        this.validateQueryAgainstColumnStructure(query, dataTable);
        dataTable = QueryEngine.executeQuery(query, dataTable, locale);
        dataTable.setLocaleForUserMessages(locale);
        return dataTable;
    }

    /**
     * Splits the <code>Query</code> object into two queries according to the declared data source
     * capabilities: data source query and completion query.
     * <p/>
     * The data source query is executed first by the data source itself. Afterward, the
     * <code>QueryEngine</code> executes the completion query over the resulting data table.
     *
     * @param query        The query to split.
     * @param capabilities The declared capabilities of the data source.
     * @return A QueryPair object.
     * @throws DataSourceException If the query cannot be split.
     */
    protected QueryPair splitQuery(Query query, Capabilities capabilities)
            throws DataSourceException {
        return QuerySplitter.splitQuery(query, capabilities);
    }

    /**
     * Checks that the query is valid against the structure of the data table.
     * A query is invalid if:
     * <ol>
     * <li> The query references column ids that don't exist in the data table.
     * <li> The query contains calculated columns operations (i.e., scalar function, aggregations)
     * that do not match the relevant columns type.
     * </ol>
     * <p/>
     * Note: does NOT validate the query itself, i.e. errors like "SELECT a, a" or
     * "SELECT a GROUP BY a" will not be caught. These kind of errors should be checked elsewhere
     * (preferably by the <code>Query.validate()</code> method).
     *
     * @param query     The query to check for validity.
     * @param dataTable The data table against which to validate. Only the columns are used.
     * @throws InvalidQueryException Thrown if the query is found to be invalid
     *                               against the given data table.
     */
    protected void validateQueryAgainstColumnStructure(Query query, DataTable dataTable)
            throws InvalidQueryException {
        // Check that all the simple columns exist in the table (including the
        // simple columns inside aggregation and scalar-function columns)
        Set<String> mentionedColumnIds = query.getAllColumnIds();
        for(String columnId : mentionedColumnIds) {
            if(!dataTable.containsColumn(columnId)) {
                String messageToLogAndUser = MessagesEnum.NO_COLUMN.getMessageWithArgs(
                        dataTable.getLocaleForUserMessages(), columnId);
                log.error(messageToLogAndUser);
                throw new InvalidQueryException(messageToLogAndUser);
            }
        }

        // Check that all aggregation columns are valid (i.e., the aggregation type
        // matches the columns type).
        Set<AggregationColumn> mentionedAggregations = query.getAllAggregations();
        for(AggregationColumn agg : mentionedAggregations) {
            try {
                agg.validateColumn(dataTable);
            }
            catch(RuntimeException e) {
                log.error("A runtime exception has occured", e);
                throw new InvalidQueryException(e.getMessage());
            }
        }

        // Check that all scalar function columns are valid. (i.e., the scalar
        // function matches the columns types).
        Set<ScalarFunctionColumn> mentionedScalarFunctionColumns =
                query.getAllScalarFunctionsColumns();
        for(ScalarFunctionColumn col : mentionedScalarFunctionColumns) {
            col.validateColumn(dataTable);
        }
    }
}
TOP

Related Classes of com.google.visualization.datasource.DataSourceService

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.