pt.webdetails.cdf.dd.api.RenderApi.java Source code

Java tutorial

Introduction

Here is the source code for pt.webdetails.cdf.dd.api.RenderApi.java

Source

/*!
 * Copyright 2002 - 2016 Webdetails, a Pentaho company. All rights reserved.
 *
 * This software was developed by Webdetails and is provided under the terms
 * of the Mozilla Public License, Version 2.0, or any later version. You may not use
 * this file except in compliance with the license. If you need a copy of the license,
 * please go to http://mozilla.org/MPL/2.0/. The Initial Developer is Webdetails.
 *
 * Software distributed under the Mozilla Public License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
 * the license for the specific language governing your rights and limitations.
 */

package pt.webdetails.cdf.dd.api;

import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
import static javax.ws.rs.core.MediaType.TEXT_HTML;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static pt.webdetails.cpf.utils.MimeTypes.JAVASCRIPT;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.pentaho.platform.api.engine.ILogger;
import org.pentaho.platform.api.engine.IParameterProvider;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.engine.core.solution.SimpleParameterProvider;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.util.logging.SimpleLogger;
import pt.webdetails.cdf.dd.CdeConstants;
import pt.webdetails.cdf.dd.CdeConstants.MethodParams;
import pt.webdetails.cdf.dd.CdeConstants.DashboardSupportedTypes;
import pt.webdetails.cdf.dd.CdeEngine;
import pt.webdetails.cdf.dd.DashboardManager;
import pt.webdetails.cdf.dd.ICdeEnvironment;
import pt.webdetails.cdf.dd.InterPluginBroker;
import pt.webdetails.cdf.dd.Messages;
import pt.webdetails.cdf.dd.MetaModelManager;
import pt.webdetails.cdf.dd.editor.DashboardEditor;
import pt.webdetails.cdf.dd.model.core.writer.ThingWriteException;
import pt.webdetails.cdf.dd.model.inst.writer.cdfrunjs.dashboard.CdfRunJsDashboardWriteOptions;
import pt.webdetails.cdf.dd.model.inst.writer.cdfrunjs.dashboard.CdfRunJsDashboardWriteResult;
import pt.webdetails.cdf.dd.structure.DashboardWcdfDescriptor;
import pt.webdetails.cdf.dd.util.CdeEnvironment;
import pt.webdetails.cdf.dd.util.JsonUtils;
import pt.webdetails.cdf.dd.util.Utils;
import pt.webdetails.cdf.dd.util.CorsUtil;
import pt.webdetails.cpf.Util;
import pt.webdetails.cpf.audit.CpfAuditHelper;
import pt.webdetails.cpf.localization.MessageBundlesHelper;
import pt.webdetails.cpf.repository.api.IReadAccess;
import pt.webdetails.cpf.utils.CharsetHelper;

@Path("pentaho-cdf-dd/api/renderer")
public class RenderApi {

    private static final Log logger = LogFactory.getLog(RenderApi.class);
    protected ICdeEnvironment privateEnviroment;

    @GET
    @Path("/getComponentDefinitions")
    @Produces(JAVASCRIPT)
    public String getComponentDefinitions(
            @QueryParam(MethodParams.SUPPORTS) @DefaultValue(DashboardSupportedTypes.LEGACY) String supports,
            @Context HttpServletResponse response) throws IOException {
        // Get and output the definitions
        if (!StringUtils.isEmpty(supports) && supports.equals(DashboardSupportedTypes.AMD)) {
            return MetaModelManager.getInstance().getAmdJsDefinition();
        } else {
            return MetaModelManager.getInstance().getJsDefinition();
        }
    }

    @GET
    @Path("/getContent")
    @Produces(JAVASCRIPT)
    public String getContent(@QueryParam(MethodParams.SOLUTION) @DefaultValue("") String solution,
            @QueryParam(MethodParams.PATH) @DefaultValue("") String path,
            @QueryParam(MethodParams.FILE) @DefaultValue("") String file,
            @QueryParam(MethodParams.INFERSCHEME) @DefaultValue("false") boolean inferScheme,
            @QueryParam(MethodParams.ROOT) @DefaultValue("") String root,
            @QueryParam(MethodParams.ABSOLUTE) @DefaultValue("false") boolean absolute,
            @QueryParam(MethodParams.BYPASSCACHE) @DefaultValue("false") boolean bypassCache,
            @QueryParam(MethodParams.DEBUG) @DefaultValue("false") boolean debug,
            @QueryParam(MethodParams.SCHEME) @DefaultValue("") String scheme, @Context HttpServletRequest request,
            @Context HttpServletResponse response) throws IOException, ThingWriteException {

        String schemeToUse = "";
        if (!inferScheme) {
            schemeToUse = StringUtils.isEmpty(scheme) ? request.getScheme() : scheme;
        }
        String filePath = getWcdfRelativePath(solution, path, file);

        CdfRunJsDashboardWriteResult dashboardWrite = this.loadDashboard(filePath, schemeToUse, root, absolute,
                bypassCache, debug, null);
        return dashboardWrite.getContent();
    }

    @GET
    @Path("/getHeaders")
    @Produces(TEXT_PLAIN)
    public String getHeaders(@QueryParam(MethodParams.SOLUTION) @DefaultValue("") String solution,
            @QueryParam(MethodParams.PATH) @DefaultValue("") String path,
            @QueryParam(MethodParams.FILE) @DefaultValue("") String file,
            @QueryParam(MethodParams.INFERSCHEME) @DefaultValue("false") boolean inferScheme,
            @QueryParam(MethodParams.ROOT) @DefaultValue("") String root,
            @QueryParam(MethodParams.ABSOLUTE) @DefaultValue("true") boolean absolute,
            @QueryParam(MethodParams.BYPASSCACHE) @DefaultValue("false") boolean bypassCache,
            @QueryParam(MethodParams.DEBUG) @DefaultValue("false") boolean debug,
            @QueryParam(MethodParams.SCHEME) @DefaultValue("") String scheme, @Context HttpServletRequest request,
            @Context HttpServletResponse response) throws IOException, ThingWriteException {

        String schemeToUse = "";
        if (!inferScheme) {
            schemeToUse = StringUtils.isEmpty(scheme) ? request.getScheme() : scheme;
        }
        String filePath = getWcdfRelativePath(solution, path, file);

        CdfRunJsDashboardWriteResult dashboardWrite = this.loadDashboard(filePath, schemeToUse, root, absolute,
                bypassCache, debug, null);
        return dashboardWrite.getHeader();
    }

    @GET
    @Path("/render")
    @Produces(TEXT_HTML)
    public String render(@QueryParam(MethodParams.SOLUTION) @DefaultValue("") String solution,
            @QueryParam(MethodParams.PATH) @DefaultValue("") String path,
            @QueryParam(MethodParams.FILE) @DefaultValue("") String file,
            @QueryParam(MethodParams.INFERSCHEME) @DefaultValue("false") boolean inferScheme,
            @QueryParam(MethodParams.ROOT) @DefaultValue("") String root,
            @QueryParam(MethodParams.ABSOLUTE) @DefaultValue("true") boolean absolute,
            @QueryParam(MethodParams.BYPASSCACHE) @DefaultValue("false") boolean bypassCache,
            @QueryParam(MethodParams.DEBUG) @DefaultValue("false") boolean debug,
            @QueryParam(MethodParams.SCHEME) @DefaultValue("") String scheme,
            @QueryParam(MethodParams.VIEW) @DefaultValue("") String view,
            @QueryParam(MethodParams.STYLE) @DefaultValue("") String style, @Context HttpServletRequest request)
            throws IOException {
        String schemeToUse = "";
        if (!inferScheme) {
            schemeToUse = StringUtils.isEmpty(scheme) ? request.getScheme() : scheme;
        }

        String filePath = getWcdfRelativePath(solution, path, file);
        if (StringUtils.isEmpty(filePath)) {
            return "No path provided.";
        }

        IReadAccess readAccess = Utils.getSystemOrUserReadAccess(filePath);
        if (readAccess == null) {
            return Messages.getString("XmlStructure.ERROR_011_READ_WRITE_ACCESS_EXCEPTION");
        }

        long start = System.currentTimeMillis();
        long end;
        ILogger iLogger = getAuditLogger();
        IParameterProvider requestParams = getParameterProvider(request.getParameterMap());

        UUID uuid = CpfAuditHelper.startAudit(getPluginName(), filePath, getObjectName(), this.getPentahoSession(),
                iLogger, requestParams);

        try {
            logger.info("[Timing] CDE Starting Dashboard Rendering");
            CdfRunJsDashboardWriteResult dashboard = loadDashboard(filePath, schemeToUse, root, absolute,
                    bypassCache, debug, style);

            DashboardWcdfDescriptor dashboardWcdf = DashboardWcdfDescriptor.load(filePath);
            String context = dashboardWcdf.isRequire() ? getCdfRequireContext(filePath, requestParams)
                    : getCdfContext(filePath, "", view, requestParams);
            String result = dashboard.render(context, getCdfRequireConfig(filePath, requestParams));

            //i18n token replacement
            if (!StringUtils.isEmpty(result) && !dashboardWcdf.isRequire()) {
                String msgDir = FilenameUtils.getPath(FilenameUtils.separatorsToUnix(filePath));
                msgDir = msgDir.startsWith(Util.SEPARATOR) ? msgDir : Util.SEPARATOR + msgDir;

                result = new MessageBundlesHelper(msgDir, Utils.getAppropriateReadAccess(msgDir),
                        CdeEnvironment.getPluginSystemWriter(), getEnv().getLocale(),
                        getEnv().getExtApi().getPluginStaticBaseUrl()).replaceParameters(result, null);
            }

            logger.info("[Timing] CDE Finished Dashboard Rendering: " + Utils.ellapsedSeconds(start) + "s");

            end = System.currentTimeMillis();
            CpfAuditHelper.endAudit(getPluginName(), filePath, getObjectName(), this.getPentahoSession(), iLogger,
                    start, uuid, end);

            return result;
        } catch (Exception ex) { //TODO: better error handling?
            String msg = "Could not load dashboard: " + ex.getMessage();
            logger.error(msg, ex);

            end = System.currentTimeMillis();
            CpfAuditHelper.endAudit(getPluginName(), filePath, getObjectName(), this.getPentahoSession(), iLogger,
                    start, uuid, end);

            return msg;
        }
    }

    @GET
    @Path("/getDashboard")
    @Produces(TEXT_HTML)
    public String getDashboard(@QueryParam(MethodParams.PATH) @DefaultValue("") String path,
            @QueryParam(MethodParams.INFERSCHEME) @DefaultValue("false") boolean inferScheme,
            @QueryParam(MethodParams.ROOT) @DefaultValue("") String root,
            @QueryParam(MethodParams.ABSOLUTE) @DefaultValue("true") boolean absolute,
            @QueryParam(MethodParams.BYPASSCACHE) @DefaultValue("false") boolean bypassCache,
            @QueryParam(MethodParams.DEBUG) @DefaultValue("false") boolean debug,
            @QueryParam(MethodParams.SCHEME) @DefaultValue("") String scheme,
            @QueryParam(MethodParams.VIEW) @DefaultValue("") String view,
            @QueryParam(MethodParams.STYLE) @DefaultValue("") String style,
            @QueryParam(MethodParams.ALIAS) @DefaultValue("") String alias, @Context HttpServletRequest request)
            throws IOException {
        final String schemeToUse;
        if (!inferScheme) {
            schemeToUse = StringUtils.isEmpty(scheme) ? request.getScheme() : scheme;
        } else {
            schemeToUse = "";
        }

        if (StringUtils.isEmpty(path)) {
            logger.warn("No path provided.");
            return "No path provided.";
        }

        IReadAccess readAccess = Utils.getSystemOrUserReadAccess(path);
        if (readAccess == null) {
            logger.warn("Access Denied or File Not Found.");
            return "Access Denied or File Not Found.";
        }

        long start = System.currentTimeMillis();
        long end;
        ILogger iLogger = getAuditLogger();
        IParameterProvider requestParams = getParameterProvider(request.getParameterMap());

        UUID uuid = CpfAuditHelper.startAudit(getPluginName(), path, getObjectName(), this.getPentahoSession(),
                iLogger, requestParams);

        try {
            logger.info("[Timing] CDE Starting To Generate Dashboard AMD Module");
            String config = getCdfRequireConfig(path, requestParams);
            CdfRunJsDashboardWriteResult dashboard = getDashboardModule(path, schemeToUse, root, absolute,
                    bypassCache, debug, style, alias);

            //TODO: how to process i18n for a required dashboard
            //i18n token replacement

            logger.info(
                    "[Timing] CDE Finished Generating Dashboard AMD Module: " + Utils.ellapsedSeconds(start) + "s");

            end = System.currentTimeMillis();
            CpfAuditHelper.endAudit(getPluginName(), path, getObjectName(), this.getPentahoSession(), iLogger,
                    start, uuid, end);

            return dashboard.getContent(config);
        } catch (Exception ex) { //TODO: better error handling?
            String msg = "Could not load dashboard: " + ex.getMessage();
            logger.error(msg, ex);

            end = System.currentTimeMillis();
            CpfAuditHelper.endAudit(getPluginName(), path, getObjectName(), this.getPentahoSession(), iLogger,
                    start, uuid, end);

            return msg;
        }
    }

    @GET
    @Path("/getDashboardParameters")
    @Produces(APPLICATION_JSON)
    public String getDashboardParameters(@QueryParam(MethodParams.PATH) @DefaultValue("") String path,
            @QueryParam(MethodParams.BYPASSCACHE) @DefaultValue("false") boolean bypassCache,
            @QueryParam(MethodParams.ALLPARAMS) @DefaultValue("false") boolean all,
            @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse)
            throws IOException {

        servletResponse.setContentType(APPLICATION_JSON);
        servletResponse.setCharacterEncoding(CharsetHelper.getEncoding());
        setCorsHeaders(servletRequest, servletResponse);

        if (StringUtils.isEmpty(path)) {
            logger.warn("No path provided.");
            return "No path provided.";
        }

        if (!hasSystemOrUserReadAccess(path)) {
            logger.warn("Access Denied or File Not Found.");
            return "Access Denied or File Not Found.";
        }

        try {
            return getDashboardManager().getDashboardParameters(path, bypassCache, all);
        } catch (Exception ex) { //TODO: better error handling?
            String msg = "Could not load dashboard parameters: " + ex.getMessage();
            logger.error(msg, ex);
            return msg;
        }
    }

    @GET
    @Path("/getDashboardDatasources")
    @Produces(APPLICATION_JSON)
    public String getDashboardDatasources(@QueryParam(MethodParams.PATH) @DefaultValue("") String path,
            @QueryParam(MethodParams.BYPASSCACHE) @DefaultValue("false") boolean bypassCache,
            @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse)
            throws IOException, JSONException {

        servletResponse.setContentType(APPLICATION_JSON);
        servletResponse.setCharacterEncoding(CharsetHelper.getEncoding());
        setCorsHeaders(servletRequest, servletResponse);

        if (StringUtils.isEmpty(path)) {
            logger.warn("No path provided.");
            return JsonUtils.getJsonResult(false, "No path provided");
        }

        if (!hasSystemOrUserReadAccess(path)) {
            logger.warn("Access Denied or File Not Found.");
            return JsonUtils.getJsonResult(false, "Access Denied or File Not Found.");
        }

        try {
            return getDashboardManager().getDashboardDataSources(path, bypassCache);
        } catch (Exception ex) { //TODO: better error handling?
            String msg = "Could not load dashboard datasources: " + ex.getMessage();
            logger.error(msg, ex);
            return JsonUtils.getJsonResult(false, msg);
        }
    }

    @GET
    @Path("/edit")
    @Produces(TEXT_HTML)
    public String edit(@QueryParam(MethodParams.SOLUTION) @DefaultValue("") String solution,
            @QueryParam(MethodParams.PATH) @DefaultValue("") String path,
            @QueryParam(MethodParams.FILE) @DefaultValue("") String file,
            @QueryParam(MethodParams.DEBUG) @DefaultValue("false") boolean debug,
            @QueryParam("isDefault") @DefaultValue("false") boolean isDefault, @Context HttpServletRequest request,
            @Context HttpServletResponse response) throws Exception {

        String wcdfPath = getWcdfRelativePath(solution, path, file);

        if (!CdeEnvironment.canCreateContent()) {
            return "This functionality is limited to users with permission 'Create Content'";
        } else if (Utils.getSystemOrUserRWAccess(wcdfPath) == null) {
            return "Access Denied or file not found - " + wcdfPath; //TODO: keep html?
        }

        return getEditor(wcdfPath, debug, request.getScheme(), isDefault, response,
                DashboardWcdfDescriptor.load(wcdfPath).isRequire());
    }

    @GET
    @Path("/new")
    @Produces(TEXT_HTML)
    public String newDashboard( //TODO: change file to path; does new ever use this arg?
            //      @QueryParam( MethodParams.SOLUTION ) @DefaultValue( "null" ) String solution,
            @QueryParam(MethodParams.PATH) @DefaultValue("") String path,
            //      @QueryParam( MethodParams.FILE ) @DefaultValue( "null" ) String file,
            @QueryParam(MethodParams.DEBUG) @DefaultValue("false") boolean debug,
            @QueryParam("isDefault") @DefaultValue("false") boolean isDefault, @Context HttpServletRequest request,
            @Context HttpServletResponse response) throws Exception {

        if (!CdeEnvironment.canCreateContent()) {
            return "This functionality is limited to users with permission 'Create Content'";
        }
        return getEditor(path, debug, request.getScheme(), isDefault, response, false);
    }

    @GET
    @Path("/listRenderers")
    @Produces(APPLICATION_JSON)
    public String listRenderers() {
        return "{\"result\": [\"" + DashboardWcdfDescriptor.DashboardRendererType.BLUEPRINT.getType() + "\",\""
                + DashboardWcdfDescriptor.DashboardRendererType.MOBILE.getType() + "\",\""
                + DashboardWcdfDescriptor.DashboardRendererType.BOOTSTRAP.getType() + "\"]}";
    }

    @GET
    @Path("/refresh")
    @Produces(TEXT_PLAIN)
    public String refresh(@Context HttpServletResponse servletResponse) throws Exception {
        String msg = "Refreshed CDE Successfully";

        try {
            getDashboardManager().refreshAll();
        } catch (Exception re) {
            msg = "Method refresh failed while trying to execute.";

            logger.error(msg, re);
            servletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
        }

        return msg;
    }

    @GET
    @Path("/cde-embed.js")
    @Produces(JAVASCRIPT)
    public String getCdeEmbeddedContext(@Context HttpServletRequest servletRequest,
            @Context HttpServletResponse servletResponse) throws Exception {
        return InterPluginBroker.getCdfEmbed(servletRequest.getProtocol(), servletRequest.getServerName(),
                servletRequest.getServerPort(), servletRequest.getSession().getMaxInactiveInterval(),
                servletRequest.getParameter("locale"), getParameterProvider(servletRequest.getParameterMap()));
    }

    private CdfRunJsDashboardWriteResult loadDashboard(String filePath, String scheme, String root,
            boolean absolute, boolean bypassCache, boolean debug, String style) throws ThingWriteException {

        CdfRunJsDashboardWriteOptions options = new CdfRunJsDashboardWriteOptions("", false, absolute, debug, root,
                scheme);
        return getDashboardManager().getDashboardCdfRunJs(filePath, options, bypassCache, style);
    }

    private CdfRunJsDashboardWriteResult getDashboardModule(String path, String scheme, String root,
            boolean absolute, boolean bypassCache, boolean debug, String style, String alias)
            throws ThingWriteException, UnsupportedEncodingException {

        final String dashboardAlias;
        if (StringUtils.isEmpty(alias)) {
            dashboardAlias = FilenameUtils.removeExtension(FilenameUtils.getName(path)) + "_"
                    + CdeConstants.DASHBOARD_ALIAS_TAG;
        } else {
            dashboardAlias = FilenameUtils.removeExtension(FilenameUtils.getName(path)) + "_" + alias;

        }
        CdfRunJsDashboardWriteOptions options = new CdfRunJsDashboardWriteOptions(dashboardAlias, true, absolute,
                debug, root, scheme);

        return getDashboardManager().getDashboardCdfRunJs(path, options, bypassCache, style);

    }

    protected DashboardManager getDashboardManager() {
        return DashboardManager.getInstance();
    }

    private String getWcdfRelativePath(String solution, String path, String file) {
        //TODO: change to use path instead of file
        //    if ( !StringUtils.isEmpty( solution ) || !StringUtils.isEmpty( file ) ) {
        //      logger.warn( "Use of solution/path/file is deprecated. Use just the path argument" );
        return Util.joinPath(solution, path, file);
        //    }
        //    else return path;
        //    final String filePath = "/" + solution + "/" + path + "/" + file;
        //    return filePath.replaceAll( "//+", "/" );
    }

    private IPentahoSession getPentahoSession() {
        return PentahoSessionHolder.getSession();
    }

    private String getObjectName() {
        return RenderApi.class.getName();
    }

    private String getPluginName() {
        return CdeEnvironment.getPluginId();
    }

    private ILogger getAuditLogger() {
        return new SimpleLogger(RenderApi.class.getName());
    }

    private IParameterProvider getParameterProvider(Map<String, String> params) {
        return new SimpleParameterProvider(params);
    }

    private String getEditor(String path, boolean debug, String scheme, boolean isDefault,
            HttpServletResponse response, boolean isRequire) throws Exception {
        response.setContentType(TEXT_HTML);
        String result = DashboardEditor.getEditor(path, debug, scheme, isDefault, isRequire);

        //i18n token replacement
        if (!StringUtils.isEmpty(result)) {

            /* cde editor's i18n is different; it continues on relying on pentaho-cdf-dd/lang/messages.properties */

            String msgDir = Util.SEPARATOR + "lang" + Util.SEPARATOR;
            result = new MessageBundlesHelper(msgDir, CdeEnvironment.getPluginSystemReader(null),
                    CdeEnvironment.getPluginSystemWriter(), getEnv().getLocale(),
                    getEnv().getExtApi().getPluginStaticBaseUrl()).replaceParameters(result, null);
        }

        return result;
    }

    private ICdeEnvironment getEnv() {
        if (this.privateEnviroment != null) {
            return this.privateEnviroment;
        }
        return CdeEngine.getEnv();
    }

    protected void setCorsHeaders(HttpServletRequest request, HttpServletResponse response) {
        CorsUtil.getInstance().setCorsHeaders(request, response);
    }

    protected boolean hasSystemOrUserReadAccess(String path) {
        return Utils.getSystemOrUserReadAccess(path) != null;
    }

    protected String getCdfRequireConfig(String filePath, IParameterProvider requestParams) throws Exception {
        return InterPluginBroker.getCdfRequireConfig(filePath, requestParams);
    }

    protected String getCdfRequireContext(String filePath, IParameterProvider requestParams) throws Exception {
        return InterPluginBroker.getCdfRequireContext(filePath, requestParams);
    }

    protected String getCdfContext(String filePath, String action, String view, IParameterProvider requestParams)
            throws Exception {
        return InterPluginBroker.getCdfContext(filePath, action, view, requestParams);
    }
}