org.apache.hawq.pxf.plugins.hdfs.WritableResolver.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hawq.pxf.plugins.hdfs.WritableResolver.java

Source

package org.apache.hawq.pxf.plugins.hdfs;

/*
 * 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 org.apache.hawq.pxf.api.*;
import org.apache.hawq.pxf.api.io.DataType;
import org.apache.hawq.pxf.api.utilities.InputData;
import org.apache.hawq.pxf.api.utilities.Plugin;
import org.apache.hawq.pxf.plugins.hdfs.utilities.RecordkeyAdapter;
import org.apache.hawq.pxf.plugins.hdfs.utilities.DataSchemaException;
import org.apache.hawq.pxf.service.utilities.Utilities;

import static org.apache.hawq.pxf.plugins.hdfs.utilities.DataSchemaException.MessageFmt.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.io.Writable;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;

import static org.apache.hawq.pxf.api.io.DataType.*;

/**
 * WritableResolver handles serialization and deserialization of records
 * that were serialized using Hadoop's Writable serialization framework.
 *
 * A field named 'recordkey' is treated as a key of the given row, and not as
 * part of the data schema. See {@link RecordkeyAdapter}.
 */
public class WritableResolver extends Plugin implements ReadResolver, WriteResolver {
    private static final int RECORDKEY_UNDEFINED = -1;
    private static final Log LOG = LogFactory.getLog(WritableResolver.class);
    private RecordkeyAdapter recordkeyAdapter = new RecordkeyAdapter();
    private int recordkeyIndex;
    // reflection fields
    private Object userObject = null;
    private Field[] fields = null;

    /**
     * Constructs a WritableResolver.
     *
     * @param input all input parameters coming from the client
     * @throws Exception if schema file is missing, cannot be found in
     *                   classpath or fails to instantiate
     */
    public WritableResolver(InputData input) throws Exception {
        super(input);

        String schemaName = inputData.getUserProperty("DATA-SCHEMA");

        /** Testing that the schema name was supplied by the user - schema is an optional property. */
        if (schemaName == null) {
            throw new DataSchemaException(SCHEMA_NOT_INDICATED, this.getClass().getName());
        }

        /** Testing that the schema resource exists. */
        if (!isSchemaOnClasspath(schemaName)) {
            throw new DataSchemaException(SCHEMA_NOT_ON_CLASSPATH, schemaName);
        }

        userObject = Utilities.createAnyInstance(schemaName);
        fields = userObject.getClass().getDeclaredFields();
        recordkeyIndex = (inputData.getRecordkeyColumn() == null) ? RECORDKEY_UNDEFINED
                : inputData.getRecordkeyColumn().columnIndex();

        // fields details:
        if (LOG.isDebugEnabled()) {
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                String javaType = field.getType().getName();
                boolean isPrivate = Modifier.isPrivate(field.getModifiers());

                LOG.debug("Field #" + i + ", name: " + field.getName() + " type: " + javaType + ", "
                        + (isArray(javaType) ? "Array" : "Primitive") + ", "
                        + (isPrivate ? "Private" : "accessible") + " field");
            }
        }
    }

    private boolean isArray(String javaType) {
        return (javaType.startsWith("[") && !"[B".equals(javaType));
    }

    @Override
    public List<OneField> getFields(OneRow onerow) throws Exception {
        userObject = onerow.getData();
        List<OneField> record = new LinkedList<OneField>();

        int currentIdx = 0;
        for (Field field : fields) {
            if (currentIdx == recordkeyIndex) {
                currentIdx += recordkeyAdapter.appendRecordkeyField(record, inputData, onerow);
            }

            if (Modifier.isPrivate(field.getModifiers())) {
                continue;
            }

            currentIdx += populateRecord(record, field);
        }

        return record;
    }

    int setArrayField(List<OneField> record, int dataType, Field reflectedField) throws IllegalAccessException {
        Object array = reflectedField.get(userObject);
        int length = Array.getLength(array);
        for (int j = 0; j < length; j++) {
            record.add(new OneField(dataType, Array.get(array, j)));
        }
        return length;
    }

    /*
     * Given a java Object type, convert it to the corresponding output field
     * type.
     */
    private DataType convertJavaToGPDBType(String type) {
        if ("boolean".equals(type) || "[Z".equals(type)) {
            return BOOLEAN;
        }
        if ("int".equals(type) || "[I".equals(type)) {
            return INTEGER;
        }
        if ("double".equals(type) || "[D".equals(type)) {
            return FLOAT8;
        }
        if ("java.lang.String".equals(type) || "[Ljava.lang.String;".equals(type)) {
            return TEXT;
        }
        if ("float".equals(type) || "[F".equals(type)) {
            return REAL;
        }
        if ("long".equals(type) || "[J".equals(type)) {
            return BIGINT;
        }
        if ("[B".equals(type)) {
            return BYTEA;
        }
        if ("short".equals(type) || "[S".equals(type)) {
            return SMALLINT;
        }
        throw new UnsupportedTypeException("Type " + type + " is not supported by GPDBWritable");
    }

    int populateRecord(List<OneField> record, Field field) throws BadRecordException {
        String javaType = field.getType().getName();
        try {
            DataType dataType = convertJavaToGPDBType(javaType);
            if (isArray(javaType)) {
                return setArrayField(record, dataType.getOID(), field);
            }
            record.add(new OneField(dataType.getOID(), field.get(userObject)));
            return 1;
        } catch (IllegalAccessException ex) {
            throw new BadRecordException(ex);
        }
    }

    /**
     * Sets customWritable fields and creates a OneRow object.
     */
    @Override
    public OneRow setFields(List<OneField> record) throws Exception {
        Writable key = null;

        int colIdx = 0;
        for (Field field : fields) {
            /*
             * extract recordkey based on the column descriptor type
             * and add to OneRow.key
             */
            if (colIdx == recordkeyIndex) {
                key = recordkeyAdapter.convertKeyValue(record.get(colIdx).val);
                colIdx++;
            }

            if (Modifier.isPrivate(field.getModifiers())) {
                continue;
            }

            String javaType = field.getType().getName();
            convertJavaToGPDBType(javaType);
            if (isArray(javaType)) {
                Object value = field.get(userObject);
                int length = Array.getLength(value);
                for (int j = 0; j < length; j++, colIdx++) {
                    Array.set(value, j, record.get(colIdx).val);
                }
            } else {
                field.set(userObject, record.get(colIdx).val);
                colIdx++;
            }
        }

        return new OneRow(key, userObject);
    }

    /*
     * Tests for the case schema resource is a file like avro_schema.avsc
     * or for the case schema resource is a Java class. in which case we try to reflect the class name.
     */
    private boolean isSchemaOnClasspath(String resource) {
        if (this.getClass().getClassLoader().getResource("/" + resource) != null) {
            return true;
        }

        try {
            Class.forName(resource);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}