org.pentaho.platform.plugin.action.ActionParams.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.platform.plugin.action.ActionParams.java

Source

/*!
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
 * Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
 * or from the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 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 Lesser General Public License for more details.
 *
 * Copyright (c) 2017 Pentaho Corporation.  All rights reserved.
 */

package org.pentaho.platform.plugin.action;

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.action.ActionInvocationException;
import org.pentaho.platform.api.action.IAction;
import org.pentaho.platform.plugin.action.messages.Messages;
import org.pentaho.platform.util.ActionUtil;
import org.pentaho.platform.web.http.api.resources.RepositoryFileStreamProvider;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This is a POJO representing the parameters coming in to the ActionResource.runInBackground()
 * endpoint. The incoming json fromatted params should be converted and used in the runInBackground method
 * singnature.
 * <p>
 * This class provides logic to serialize and de-serialize the action parameters.
 * TODO: in the future investigate replacing this class with generic platform API if any.
 */
@JsonRootName("ActionParams")
public class ActionParams {
    private static final Log logger = LogFactory.getLog(ActionParams.class);

    /**
     * The serialized params are encapsulated in a serialized json by jackson fastxml library.
     * (which provide a generic way to encapsulate a map of <String,Serializable>
     */
    private String serializedParams;

    /**
     * Some of the action parameters may not be serializable or be meaningless to pass around.
     * For this the name of those are captured so that they could be recreated as needed
     * on the other side.
     */
    private List<String> paramsToRecreate = new ArrayList<>();

    public String getSerializedParams() {
        return serializedParams;
    }

    public void setSerializedParams(String serializedParams) {
        this.serializedParams = serializedParams;
    }

    public List<String> getParamsToRecreate() {
        return paramsToRecreate;
    }

    public void setParamsToRecreate(final List<String> paramsToRecreate) {
        this.paramsToRecreate = paramsToRecreate;
    }

    /**
     * Empty constructor, required for jackson library to work properly
     */
    public ActionParams() {
    }

    /**
     * Private constructor used by the class only.
     *
     * @param serializedParams   the params that were serialized in the action request.
     * @param unserializedParams the params that were not serialized and potentially need to be recreated to execute the
     *                           action.
     */
    private ActionParams(final String serializedParams, List<String> unserializedParams) {
        this.serializedParams = serializedParams;
        this.paramsToRecreate = unserializedParams;
    }

    /**
     * Serialize an action parameters.
     *
     * @param action the action.
     * @param params the parameters
     * @return an ActionParams instance.
     * @throws ActionInvocationException when serialization fails for any reason.
     */
    public static ActionParams serialize(final IAction action, final Map<String, Serializable> params)
            throws ActionInvocationException {

        // since we end up filtering the un-serialized params, use a clone
        //
        final Map<String, Serializable> clonedParams = new HashMap<>(params);
        final List<String> paramsToRecreate = filter(action, clonedParams);

        try {
            return new ActionParams(serializeParams(clonedParams), paramsToRecreate);
        } catch (final JsonProcessingException ex) {
            throw new ActionInvocationException("Failed to serialize action params", ex);
        }
    }

    /**
     * De-serialize ActionParams. That is, convert them back to a map as the code down stream expects.
     *
     * @param action the action these params belong too.
     * @param params the ActionParams instance to deserialize.
     * @return a map of parameters, with String keys and values that are all serializable.
     * @throws ActionInvocationException if anything goes wrong.
     */
    public static Map<String, Serializable> deserialize(final IAction action, final ActionParams params)
            throws ActionInvocationException {
        try {
            Map<String, Serializable> res = deserializeParams(params.getSerializedParams());
            // at this point recreate the unserialized parameters, as needed
            //
            recreate(params.getParamsToRecreate(), res);
            return res;
        } catch (final Exception ex) {
            // TODO: localize
            throw new ActionInvocationException("Action Parameters could not be deserialized.");
        }
    }

    /**
     * A conversion to a json string form an ActionParams instance.
     *
     * @param params the instance to convert from.
     * @return a json formatted String.
     * @throws JsonProcessingException if the conversion fails.
     */
    public static String toJson(final ActionParams params) throws JsonProcessingException {
        return new ObjectMapper().writeValueAsString(params);
    }

    /**
     * A conversion from a json string to an ActionParams instance.
     *
     * @param params the json string to convert from.
     * @return an ActionParams instance.
     * @throws IOException on a json string parsing error
     */
    public static ActionParams fromJson(final String params) throws IOException {
        return new ObjectMapper().readValue(params, ActionParams.class);
    }

    /**
     * Recreates the un-serializable params of an action. A inverse sibling of filter().
     *
     * @param paramsToRecreate a list with the names of the un-serializable parameters.
     * @param res              the current map parameters where the un-serialized parameters will be recreated and added
     *                         to.
     */
    private static void recreate(final List<String> paramsToRecreate, final Map<String, Serializable> res) {
        for (final String param : paramsToRecreate) {
            if (param.equals(ActionUtil.INVOKER_STREAMPROVIDER)) {
                recreateStreamProvider(res);
            }
        }
    }

    private static void recreateStreamProvider(final Map<String, Serializable> params) {
        final Serializable spInputFile = params.get(ActionUtil.INVOKER_STREAMPROVIDER_INPUT_FILE);
        final String inputFile = spInputFile != null ? spInputFile.toString() : null;
        final Serializable spOutputFilePattern = params.get(ActionUtil.INVOKER_STREAMPROVIDER_OUTPUT_FILE_PATTERN);
        final String outputFilePattern = spOutputFilePattern != null ? spOutputFilePattern.toString() : null;

        if (inputFile == null || outputFilePattern == null) {
            if (logger.isWarnEnabled()) {
                logger.warn(Messages.getInstance().getMissingParamsCantReturnSp(
                        String.format("%s, %s", ActionUtil.INVOKER_STREAMPROVIDER_INPUT_FILE,
                                ActionUtil.INVOKER_STREAMPROVIDER_OUTPUT_FILE_PATTERN),
                        params)); //$NON-NLS-1$
            }

            return;
        }

        final boolean autoCreateUniqueFilename = params
                .containsKey(ActionUtil.INVOKER_STREAMPROVIDER_UNIQUE_FILE_NAME)
                        ? Boolean.parseBoolean(
                                params.get(ActionUtil.INVOKER_STREAMPROVIDER_UNIQUE_FILE_NAME).toString())
                        : true;

        params.put(ActionUtil.INVOKER_STREAMPROVIDER,
                new RepositoryFileStreamProvider(inputFile, outputFilePattern, autoCreateUniqueFilename));
    }

    /**
     * Removes un-serializable action parameters from the parameters map. An inverse sibling of the recreate()
     *
     * @param action the action that has these parameters.
     * @param params the parameter map to filter un-serialized parameters from.
     * @return A list holding the key names of the parameters that were filtered.
     */
    private static List<String> filter(final IAction action, Map<String, Serializable> params) {
        List<String> res = new ArrayList<>();
        for (final String name : params.keySet()) {
            if (name.equals(ActionUtil.INVOKER_STREAMPROVIDER)) {
                res.add(name);
            } else if (name.equals(ActionUtil.INVOKER_SESSION)) {
                // discard as this is a quartz context parameter that may be used only locally.
                //
                res.add(name);
            }
        }

        for (final String paramName : res) {
            params.remove(paramName);
        }

        return res;
    }

    /**
     * Convenience method for serializing a params map.
     *
     * @param params the parameter map.
     * @return the serialized representation of the map.
     * @throws JsonProcessingException if anything goes wrong.
     */
    private static String serializeParams(final Map<String, Serializable> params) throws JsonProcessingException {
        final ObjectMapper mapper = new ObjectMapper();
        // this mapper configuration is required to enable de-serialization.
        // the mapper object will inject type info into the json for this purpose.
        //
        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        return mapper.writeValueAsString(params);
    }

    /**
     * Convenience method for de-serialize a params map that was serialize with serializeParams() method.
     *
     * @param serializedParams the parameter map.
     * @return the serialized representation of the map.
     * @throws JsonProcessingException if anything goes worng.
     */
    private static Map<String, Serializable> deserializeParams(final String serializedParams) throws IOException {
        final ObjectMapper mapper = new ObjectMapper();

        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        return mapper.readValue(serializedParams, HashMap.class);
    }

    @Override
    public boolean equals(final Object other) {
        if (other == null || (!(other instanceof ActionParams))) {
            return false;
        } else if (this == other) {
            return true;
        } else {
            return serializedParams.equals(((ActionParams) other).serializedParams)
                    && paramsToRecreate.equals(((ActionParams) other).paramsToRecreate);
        }
    }

}