org.apache.hawq.pxf.service.utilities.ProtocolData.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hawq.pxf.service.utilities.ProtocolData.java

Source

package org.apache.hawq.pxf.service.utilities;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.security.UserGroupInformation;

import org.apache.hawq.pxf.api.OutputFormat;
import org.apache.hawq.pxf.api.utilities.ColumnDescriptor;
import org.apache.hawq.pxf.api.utilities.InputData;
import org.apache.hawq.pxf.api.utilities.ProfilesConf;

/**
 * Common configuration of all MetaData classes. Provides read-only access to
 * common parameters supplied using system properties.
 */
public class ProtocolData extends InputData {

    private static final String TRUE_LCASE = "true";
    private static final String FALSE_LCASE = "false";
    private static final String PROP_PREFIX = "X-GP-";
    public static final int INVALID_SPLIT_IDX = -1;

    private static final Log LOG = LogFactory.getLog(ProtocolData.class);

    protected OutputFormat outputFormat;
    protected int port;
    protected String host;
    protected String profile;
    protected String token;
    // statistics parameters
    protected int statsMaxFragments;
    protected float statsSampleRatio;

    /**
     * Constructs a ProtocolData. Parses X-GP-* configuration variables.
     *
     * @param paramsMap contains all query-specific parameters from Hawq
     */
    public ProtocolData(Map<String, String> paramsMap) {

        requestParametersMap = paramsMap;
        segmentId = getIntProperty("SEGMENT-ID");
        totalSegments = getIntProperty("SEGMENT-COUNT");
        filterStringValid = getBoolProperty("HAS-FILTER");

        if (filterStringValid) {
            filterString = getProperty("FILTER");
        }

        parseFormat(getProperty("FORMAT"));

        host = getProperty("URL-HOST");
        port = getIntProperty("URL-PORT");

        tupleDescription = new ArrayList<ColumnDescriptor>();
        recordkeyColumn = null;
        parseTupleDescription();

        /*
         * accessor - will throw exception from getPropery() if outputFormat is
         * BINARY and the user did not supply accessor=... or profile=...
         * resolver - will throw exception from getPropery() if outputFormat is
         * BINARY and the user did not supply resolver=... or profile=...
         */
        profile = getOptionalProperty("PROFILE");
        if (profile != null) {
            setProfilePlugins();
        }
        accessor = getProperty("ACCESSOR");
        resolver = getProperty("RESOLVER");
        fragmenter = getOptionalProperty("FRAGMENTER");
        dataSource = getProperty("DATA-DIR");

        /* Kerberos token information */
        if (UserGroupInformation.isSecurityEnabled()) {
            token = getProperty("TOKEN");
        }

        parseFragmentMetadata();
        parseUserData();
        parseThreadSafe();
        parseRemoteCredentials();

        dataFragment = INVALID_SPLIT_IDX;
        parseDataFragment(getOptionalProperty("DATA-FRAGMENT"));

        statsMaxFragments = 0;
        statsSampleRatio = 0;
        parseStatsParameters();

        // Store alignment for global use as a system property
        System.setProperty("greenplum.alignment", getProperty("ALIGNMENT"));
    }

    /**
     * Constructs an InputDataBuilder from a copy. Used to create from an
     * extending class.
     *
     * @param copy the input data to copy
     */
    public ProtocolData(ProtocolData copy) {
        this.requestParametersMap = copy.requestParametersMap;
        this.segmentId = copy.segmentId;
        this.totalSegments = copy.totalSegments;
        this.outputFormat = copy.outputFormat;
        this.host = copy.host;
        this.port = copy.port;
        this.fragmentMetadata = copy.fragmentMetadata;
        this.userData = copy.userData;
        this.tupleDescription = copy.tupleDescription;
        this.recordkeyColumn = copy.recordkeyColumn;
        this.filterStringValid = copy.filterStringValid;
        this.filterString = copy.filterString;
        this.dataSource = copy.dataSource;
        this.accessor = copy.accessor;
        this.resolver = copy.resolver;
        this.fragmenter = copy.fragmenter;
        this.threadSafe = copy.threadSafe;
        this.remoteLogin = copy.remoteLogin;
        this.remoteSecret = copy.remoteSecret;
        this.token = copy.token;
        this.statsMaxFragments = copy.statsMaxFragments;
        this.statsSampleRatio = copy.statsSampleRatio;
    }

    /**
     * Sets the requested profile plugins from profile file into
     * {@link #requestParametersMap}.
     */
    private void setProfilePlugins() {
        Map<String, String> pluginsMap = ProfilesConf.getProfilePluginsMap(profile);
        checkForDuplicates(pluginsMap, requestParametersMap);
        requestParametersMap.putAll(pluginsMap);
    }

    /**
     * Verifies there are no duplicates between parameters declared in the table
     * definition and parameters defined in a profile.
     *
     * The parameters' names are case insensitive.
     */
    private void checkForDuplicates(Map<String, String> plugins, Map<String, String> params) {
        List<String> duplicates = new ArrayList<>();
        for (String key : plugins.keySet()) {
            if (params.containsKey(key)) {
                duplicates.add(key);
            }
        }

        if (!duplicates.isEmpty()) {
            throw new IllegalArgumentException("Profile '" + profile + "' already defines: "
                    + String.valueOf(duplicates).replace("X-GP-", ""));
        }
    }

    /**
     * Returns the request parameters.
     *
     * @return map of request parameters
     */
    public Map<String, String> getParametersMap() {
        return requestParametersMap;
    }

    /**
     * Throws an exception when the given property value is missing in request.
     *
     * @param property missing property name
     * @throws IllegalArgumentException throws an exception with the property
     *             name in the error message
     */
    public void protocolViolation(String property) {
        String error = "Internal server error. Property \"" + property + "\" has no value in current request";

        LOG.error(error);
        throw new IllegalArgumentException(error);
    }

    /**
     * Returns the value to which the specified property is mapped in
     * {@link #requestParametersMap}.
     *
     * @param property the lookup property key
     * @throws IllegalArgumentException if property key is missing
     */
    private String getProperty(String property) {
        String result = requestParametersMap.get(PROP_PREFIX + property);

        if (result == null) {
            protocolViolation(property);
        }

        return result;
    }

    /**
     * Returns the optional property value. Unlike {@link #getProperty}, it will
     * not fail if the property is not found. It will just return null instead.
     *
     * @param property the lookup optional property
     * @return property value as a String
     */
    private String getOptionalProperty(String property) {
        return requestParametersMap.get(PROP_PREFIX + property);
    }

    /**
     * Returns a property value as an int type.
     *
     * @param property the lookup property
     * @return property value as an int type
     * @throws NumberFormatException if the value is missing or can't be
     *             represented by an Integer
     */
    private int getIntProperty(String property) {
        return Integer.parseInt(getProperty(property));
    }

    /**
     * Returns a property value as boolean type. A boolean property is defined
     * as an int where 0 means false, and anything else true (like C).
     *
     * @param property the lookup property
     * @return property value as boolean
     * @throws NumberFormatException if the value is missing or can't be
     *             represented by an Integer
     */
    private boolean getBoolProperty(String property) {
        return getIntProperty(property) != 0;
    }

    /**
     * Returns the current output format, either {@link OutputFormat#TEXT} or
     * {@link OutputFormat#BINARY}.
     *
     * @return output format
     */
    public OutputFormat outputFormat() {
        return outputFormat;
    }

    /**
     * Returns the server name providing the service.
     *
     * @return server name
     */
    public String serverName() {
        return host;
    }

    /**
     * Returns the server port providing the service.
     *
     * @return server port
     */
    public int serverPort() {
        return port;
    }

    /**
     * Returns Kerberos token information.
     *
     * @return token
     */
    public String getToken() {
        return token;
    }

    /**
     * Statistics parameter. Returns the max number of fragments to return for
     * ANALYZE sampling. The value is set in HAWQ side using the GUC
     * pxf_stats_max_fragments.
     *
     * @return max number of fragments to be processed by analyze
     */
    public int getStatsMaxFragments() {
        return statsMaxFragments;
    }

    /**
     * Statistics parameter. Returns a number between 0.0001 and 1.0,
     * representing the sampling ratio on each fragment for ANALYZE sampling.
     * The value is set in HAWQ side based on ANALYZE computations and the
     * number of sampled fragments.
     *
     * @return sampling ratio
     */
    public float getStatsSampleRatio() {
        return statsSampleRatio;
    }

    /**
     * Sets the thread safe parameter. Default value - true.
     */
    private void parseThreadSafe() {

        threadSafe = true;
        String threadSafeStr = getOptionalProperty("THREAD-SAFE");
        if (threadSafeStr != null) {
            threadSafe = parseBooleanValue(threadSafeStr);
        }
    }

    private boolean parseBooleanValue(String threadSafeStr) {

        if (threadSafeStr.equalsIgnoreCase(TRUE_LCASE)) {
            return true;
        }
        if (threadSafeStr.equalsIgnoreCase(FALSE_LCASE)) {
            return false;
        }
        throw new IllegalArgumentException(
                "Illegal boolean value '" + threadSafeStr + "'." + " Usage: [TRUE|FALSE]");
    }

    /**
     * Sets the format type based on the input string. Allowed values are:
     * "TEXT", "GPDBWritable".
     *
     * @param formatString format string
     */
    protected void parseFormat(String formatString) {
        switch (formatString) {
        case "TEXT":
            outputFormat = OutputFormat.TEXT;
            break;
        case "GPDBWritable":
            outputFormat = OutputFormat.BINARY;
            break;
        default:
            throw new IllegalArgumentException("Wrong value for greenplum.format " + formatString);
        }
    }

    /*
     * Sets the tuple description for the record
     */
    void parseTupleDescription() {
        int columns = getIntProperty("ATTRS");
        for (int i = 0; i < columns; ++i) {
            String columnName = getProperty("ATTR-NAME" + i);
            int columnTypeCode = getIntProperty("ATTR-TYPECODE" + i);
            String columnTypeName = getProperty("ATTR-TYPENAME" + i);

            ColumnDescriptor column = new ColumnDescriptor(columnName, columnTypeCode, i, columnTypeName);
            tupleDescription.add(column);

            if (columnName.equalsIgnoreCase(ColumnDescriptor.RECORD_KEY_NAME)) {
                recordkeyColumn = column;
            }
        }
    }

    /**
     * Sets the index of the allocated data fragment
     *
     * @param fragment the allocated data fragment
     */
    protected void parseDataFragment(String fragment) {

        /*
         * Some resources don't require a fragment, hence the list can be empty.
         */
        if (StringUtils.isEmpty(fragment)) {
            return;
        }
        dataFragment = Integer.parseInt(fragment);
    }

    private void parseFragmentMetadata() {
        fragmentMetadata = parseBase64("FRAGMENT-METADATA", "Fragment metadata information");
    }

    private void parseUserData() {
        userData = parseBase64("FRAGMENT-USER-DATA", "Fragment user data");
    }

    private byte[] parseBase64(String key, String errName) {
        String encoded = getOptionalProperty(key);
        if (encoded == null) {
            return null;
        }
        if (!Base64.isArrayByteBase64(encoded.getBytes())) {
            throw new IllegalArgumentException(
                    errName + " must be Base64 encoded." + "(Bad value: " + encoded + ")");
        }
        byte[] parsed = Base64.decodeBase64(encoded);
        LOG.debug("decoded " + key + ": " + new String(parsed));
        return parsed;
    }

    private void parseRemoteCredentials() {
        remoteLogin = getOptionalProperty("REMOTE-USER");
        remoteSecret = getOptionalProperty("REMOTE-PASS");
    }

    private void parseStatsParameters() {

        String maxFrags = getOptionalProperty("STATS-MAX-FRAGMENTS");
        if (!StringUtils.isEmpty(maxFrags)) {
            statsMaxFragments = Integer.parseInt(maxFrags);
            if (statsMaxFragments <= 0) {
                throw new IllegalArgumentException("Wrong value '" + statsMaxFragments + "'. "
                        + "STATS-MAX-FRAGMENTS must be a positive integer");
            }
        }

        String sampleRatioStr = getUserProperty("STATS-SAMPLE-RATIO");
        if (!StringUtils.isEmpty(sampleRatioStr)) {
            statsSampleRatio = Float.parseFloat(sampleRatioStr);
            if (statsSampleRatio < 0.0001 || statsSampleRatio > 1.0) {
                throw new IllegalArgumentException("Wrong value '" + statsSampleRatio + "'. "
                        + "STATS-SAMPLE-RATIO must be a value between 0.0001 and 1.0");
            }
        }

        if ((statsSampleRatio > 0) != (statsMaxFragments > 0)) {
            throw new IllegalArgumentException(
                    "Missing parameter: STATS-SAMPLE-RATIO and STATS-MAX-FRAGMENTS must be set together");
        }
    }
}