pt.webdetails.cpf.SimpleContentGenerator.java Source code

Java tutorial

Introduction

Here is the source code for pt.webdetails.cpf.SimpleContentGenerator.java

Source

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

package pt.webdetails.cpf;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeoutException;

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

import mondrian.olap.QueryTimeoutException;

import org.apache.commons.io.IOUtils;
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.IMimeTypeListener;
import org.pentaho.platform.api.engine.IOutputHandler;
import org.pentaho.platform.api.engine.IParameterProvider;
import org.pentaho.platform.api.repository.IContentItem;

import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.security.SecurityHelper;
import org.pentaho.platform.engine.services.solution.BaseContentGenerator;
import org.springframework.security.GrantedAuthorityImpl;

import pt.webdetails.cpf.annotations.AccessLevel;
import pt.webdetails.cpf.annotations.Audited;
import pt.webdetails.cpf.annotations.Exposed;
import pt.webdetails.cpf.audit.CpfAuditHelper;
import pt.webdetails.cpf.messaging.JsonSerializable;
//import pt.webdetails.cpf.repository.PentahoRepositoryAccess;
import pt.webdetails.cpf.utils.CharsetHelper;

/**
 *
 * @author pdpi
 */
public abstract class SimpleContentGenerator extends BaseContentGenerator {

    private static final long serialVersionUID = 1L;
    protected Log logger = LogFactory.getLog(this.getClass());

    protected static final String ENCODING = CharsetHelper.getEncoding();

    protected static String getEncoding() {
        return ENCODING;
    }

    public enum FileType {
        JPG, JPEG, PNG, GIF, BMP, JS, CSS, HTML, HTM, XML, SVG, PDF, TXT, DOC, DOCX, XLS, XLSX, PPT, PPTX;

        public static FileType parse(String value) {
            return valueOf(StringUtils.upperCase(value));
        }
    }

    public static class MimeType {
        public static final String CSS = "text/css";
        public static final String JAVASCRIPT = "text/javascript";
        public static final String PLAIN_TEXT = "text/plain";
        public static final String HTML = "text/html";
        public static final String XML = "text/xml";
        public static final String JPEG = "img/jpeg";
        public static final String PNG = "image/png";
        public static final String GIF = "image/gif";
        public static final String BMP = "image/bmp";
        public static final String JSON = "application/json";
        public static final String PDF = "application/pdf";

        public static final String DOC = "application/msword";
        public static final String DOCX = "application/msword";

        public static final String XLS = "application/msexcel";
        public static final String XLSX = "application/msexcel";

        public static final String PPT = "application/mspowerpoint";
        public static final String PPTX = "application/mspowerpoint";
    }

    protected static final EnumMap<FileType, String> mimeTypes = new EnumMap<FileType, String>(FileType.class);

    static {
        /*
         * Image types
         */
        mimeTypes.put(FileType.JPG, MimeType.JPEG);
        mimeTypes.put(FileType.JPEG, MimeType.JPEG);
        mimeTypes.put(FileType.PNG, MimeType.PNG);
        mimeTypes.put(FileType.GIF, MimeType.GIF);
        mimeTypes.put(FileType.BMP, MimeType.BMP);

        /*
         * HTML (and related) types
         */
        // Deprecated, should be application/javascript, but IE doesn't like that
        mimeTypes.put(FileType.JS, MimeType.JAVASCRIPT);
        mimeTypes.put(FileType.HTM, MimeType.HTML);
        mimeTypes.put(FileType.HTML, MimeType.HTML);
        mimeTypes.put(FileType.CSS, MimeType.CSS);
        mimeTypes.put(FileType.XML, MimeType.XML);
        mimeTypes.put(FileType.TXT, MimeType.PLAIN_TEXT);
    }

    protected String getMimeType(String fileName) {
        String[] fileNameSplit = StringUtils.split(fileName, '.');// fileName.split("\\.");
        try {
            return getMimeType(FileType.valueOf(fileNameSplit[fileNameSplit.length - 1].toUpperCase()));
        } catch (Exception e) {
            logger.warn("Unrecognized extension for file name " + fileName);
            return "";
        }
    }

    protected String getMimeType(FileType fileType) {
        if (fileType == null)
            return "";
        String mimeType = mimeTypes.get(fileType);
        return mimeType == null ? "" : mimeType;
    }

    @Override
    public void createContent() throws Exception {
        IParameterProvider pathParams = getPathParameters();// parameterProviders.get("path");

        try {

            String path = pathParams.getStringParameter("path", null);
            String[] pathSections = StringUtils.split(path, "/");

            if (pathSections == null || pathSections.length == 0) {
                String method = getDefaultPath(path);
                if (!StringUtils.isEmpty(method)) {
                    logger.warn("No method supplied, redirecting.");
                    redirect(method);
                } else {
                    logger.error("No method supplied.");
                }
            } else {

                final String methodName = pathSections[0];

                try {

                    final Method method = getMethod(methodName);
                    invokeMethod(methodName, method);

                } catch (NoSuchMethodException e) {
                    String msg = "couldn't locate method: " + methodName;
                    logger.warn(msg);
                    getResponse().sendError(HttpServletResponse.SC_NOT_FOUND, msg);
                } catch (InvocationTargetException e) {
                    // get to the cause and log properly
                    Throwable cause = e.getCause();
                    if (cause == null)
                        cause = e;
                    handleError(methodName, cause);
                } catch (Exception e) {
                    handleError(methodName, e);
                }

            }
        } catch (SecurityException e) {
            logger.warn(e.toString());
        }
    }

    private void handleError(final String methodName, Throwable e) throws IOException {

        logger.error(methodName + ": " + e.getMessage(), e);

        String msg = e.getLocalizedMessage();
        if (e instanceof QueryTimeoutException || e instanceof TimeoutException)
        //          ||
        //          (e instanceof RuntimeException &&
        //              StringUtils.containsIgnoreCase(e.getClass().getName(), "timeout"))) 
        {
            getResponse().sendError(HttpServletResponse.SC_REQUEST_TIMEOUT, msg);
        } else {// default to 500
            getResponse().sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
        }
    }

    /**
     * @param methodName
     * @return
     * @throws NoSuchMethodException
     */
    protected Method getMethod(final String methodName) throws NoSuchMethodException {
        final Class<?>[] params = getCGMethodParams();
        final Method method = this.getClass().getMethod(methodName, params);
        return method;
    }

    /**
     * @return this plugin's name
     */
    public abstract String getPluginName();

    /**
     * @return this plugin's path
     */
    public String getPluginPath() {
        //      return PentahoRepositoryAccess.getSystemDir() + "/" + getPluginName();
        //FIXME COMPILING use plugin-res
        return null;
    }

    /**
     * Get a map of all public methods with the Exposed annotation.
     * Map is not thread-safe and should be used read-only.
     * @param classe Class where to find methods
     * @param log classe's logger
     * @param lowerCase if keys should be in lower case.
     * @return map of all public methods with the Exposed annotation
     */
    protected static Map<String, Method> getExposedMethods(Class<?> classe, boolean lowerCase) {
        HashMap<String, Method> exposedMethods = new HashMap<String, Method>();
        Log log = LogFactory.getLog(classe);
        for (Method method : classe.getMethods()) {
            if (method.getAnnotation(Exposed.class) != null) {
                String methodKey = method.getName().toLowerCase();
                if (exposedMethods.containsKey(methodKey)) {
                    log.error("Method " + method + " differs from " + exposedMethods.get(methodKey)
                            + " only in case and will override calls to it!!");
                }
                log.debug("registering " + classe.getSimpleName() + "." + method.getName());
                exposedMethods.put(methodKey, method);
            }
        }
        return exposedMethods;
    }

    /**
     * In case we need to use reflection with methods that don't just take the OutputStream parameter.
     * @return classes of exposed methods parameters
     */
    protected Class<?>[] getCGMethodParams() {
        return new Class<?>[] { OutputStream.class };
    }

    protected OutputStream getResponseOutputStream(final String mimeType) throws IOException {
        IContentItem contentItem = outputHandler.getOutputContentItem(IOutputHandler.RESPONSE,
                IOutputHandler.CONTENT, instanceId, mimeType);
        return contentItem.getOutputStream(null);
    }

    protected HttpServletRequest getRequest() {
        return (HttpServletRequest) parameterProviders.get("path").getParameter("httprequest");
    }

    protected HttpServletResponse getResponse() {
        return (HttpServletResponse) parameterProviders.get("path").getParameter("httpresponse");
    }

    protected IParameterProvider getRequestParameters() {
        return parameterProviders.get("request");
    }

    protected IParameterProvider getPathParameters() {
        return parameterProviders.get("path");
    }

    protected String getRequestParameterAsString(String parameter, String defaultValue)
            throws UnsupportedEncodingException {
        return getRequestParameterAsString(parameter, defaultValue, CharsetHelper.getEncoding());
    }

    protected String getRequestParameterAsString(String parameter, String defaultValue, String encoding)
            throws UnsupportedEncodingException {

        String enc = StringUtils.isEmpty(encoding) ? CharsetHelper.getEncoding() : encoding;

        if (getRequestParameters() != null && getRequestParameters().hasParameter(parameter)) {
            return URLDecoder.decode(getRequestParameters().getStringParameter(parameter, defaultValue), enc);
        }
        return defaultValue;
    }

    protected String getPathParameterAsString(String parameter, String defaultValue)
            throws UnsupportedEncodingException {
        return getPathParameterAsString(parameter, defaultValue, CharsetHelper.getEncoding());
    }

    protected String getPathParameterAsString(String parameter, String defaultValue, String encoding)
            throws UnsupportedEncodingException {

        String enc = StringUtils.isEmpty(encoding) ? CharsetHelper.getEncoding() : encoding;

        if (getPathParameters() != null && getPathParameters().hasParameter(parameter)) {
            return URLDecoder.decode(getPathParameters().getStringParameter(parameter, defaultValue), enc);
        }
        return defaultValue;
    }

    protected String getDefaultPath(String path) {
        return null;
    }

    private boolean canAccessMethod(Method method, Exposed exposed) {
        if (exposed != null) {

            AccessLevel accessLevel = exposed.accessLevel();
            if (accessLevel != null) {

                boolean accessible = false;
                switch (accessLevel) {
                case ADMIN:
                    accessible = SecurityHelper.getInstance()
                            .isPentahoAdministrator(PentahoSessionHolder.getSession());
                    break;
                case ROLE:
                    String role = exposed.role();
                    if (!StringUtils.isEmpty(role)) {
                        accessible = SecurityHelper.getInstance().isGranted(PentahoSessionHolder.getSession(),
                                new GrantedAuthorityImpl(role));
                    }
                    break;
                case PUBLIC:
                    accessible = true;
                    break;
                default:
                    logger.error("Unsupported AccessLevel " + accessLevel);
                }

                return accessible;
            }

        }
        return false;
    }

    protected boolean invokeMethod(final String methodName, final Method method)
            throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, IOException {

        Exposed exposed = method.getAnnotation(Exposed.class);

        if (canAccessMethod(method, exposed)) {

            Audited audited = method.getAnnotation(Audited.class);
            UUID uuid = null;
            long start = System.currentTimeMillis();
            if (audited != null) {
                uuid = CpfAuditHelper.startAudit(getPluginName(), audited.action(), getObjectName(), userSession,
                        this, getRequestParameters());
            }
            final OutputStream out = getResponseOutputStream(exposed.outputType());
            setResponseHeaders(exposed.outputType());
            try {
                method.invoke(this, out);
            } finally {
                if (audited != null) {
                    CpfAuditHelper.endAudit(getPluginName(), audited.action(), getObjectName(), userSession, this,
                            start, uuid, System.currentTimeMillis());
                }
            }

            return true;
        }
        String msg = "Method " + methodName + " not exposed or user does not have required permissions.";
        logger.error(msg);
        getResponse().sendError(HttpServletResponse.SC_FORBIDDEN, msg);
        return false;
    }

    protected void redirect(String method) {

        final HttpServletResponse response = (HttpServletResponse) parameterProviders.get("path")
                .getParameter("httpresponse");

        if (response == null) {
            logger.error("response not found");
            return;
        }
        try {
            response.sendRedirect(method);
        } catch (IOException e) {
            logger.error("could not redirect", e);
        }
    }

    /**
     * Write to OutputStream using defined encoding.
     * @param out
     * @param contents
     * @throws IOException
     */
    protected void writeOut(OutputStream out, String contents) throws IOException {
        IOUtils.write(contents, out, getEncoding());
    }

    protected void writeOut(OutputStream out, JsonSerializable contents) throws IOException, JSONException {
        IOUtils.write(contents.toJSON().toString(), out, getEncoding());
    }

    @Override
    public Log getLogger() {
        return logger;
    }

    protected void setResponseHeaders(final String mimeType) {
        setResponseHeaders(mimeType, 0, null);
    }

    protected void setResponseHeaders(final String mimeType, final String attachmentName) {
        setResponseHeaders(mimeType, 0, attachmentName);
    }

    protected void setResponseHeaders(final String mimeType, final int cacheDuration, final String attachmentName) {
        // Make sure we have the correct mime type

        final IMimeTypeListener mimeTypeListener = outputHandler.getMimeTypeListener();
        if (mimeTypeListener != null) {
            mimeTypeListener.setMimeType(mimeType);
        }

        final HttpServletResponse response = getResponse();

        if (response == null) {
            logger.warn("Parameter 'httpresponse' not found!");
            return;
        }

        response.setHeader("Content-Type", mimeType);

        if (attachmentName != null) {
            response.setHeader("content-disposition", "attachment; filename=" + attachmentName);
        } // Cache?

        if (cacheDuration > 0) {
            response.setHeader("Cache-Control", "max-age=" + cacheDuration);
        } else {
            response.setHeader("Cache-Control", "max-age=0, no-store");
        }
    }

    protected void copyParametersFromProvider(Map<String, Object> params, IParameterProvider provider) {
        @SuppressWarnings("unchecked")
        Iterator<String> paramNames = provider.getParameterNames();
        while (paramNames.hasNext()) {
            String paramName = paramNames.next();
            params.put(paramName, provider.getParameter(paramName));
        }
    }
}