org.apache.gora.hbase.store.HBaseStore.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.gora.hbase.store.HBaseStore.java

Source

/**
 * 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.
 */
package org.apache.gora.hbase.store;

import static org.apache.gora.hbase.util.HBaseByteInterface.fromBytes;
import static org.apache.gora.hbase.util.HBaseByteInterface.toBytes;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Properties;
import java.util.Set;

import javax.naming.ConfigurationException;

import org.apache.avro.Schema;
import org.apache.avro.Schema.Field;
import org.apache.avro.Schema.Type;
import org.apache.avro.util.Utf8;
import org.apache.commons.io.IOUtils;
import org.apache.gora.hbase.query.HBaseGetResult;
import org.apache.gora.hbase.query.HBaseQuery;
import org.apache.gora.hbase.query.HBaseScannerResult;
import org.apache.gora.hbase.store.HBaseMapping.HBaseMappingBuilder;
import org.apache.gora.hbase.util.HBaseByteInterface;
import org.apache.gora.hbase.util.HBaseFilterUtil;
import org.apache.gora.persistency.impl.DirtyListWrapper;
import org.apache.gora.persistency.impl.DirtyMapWrapper;
import org.apache.gora.persistency.impl.PersistentBase;
import org.apache.gora.query.PartitionQuery;
import org.apache.gora.query.Query;
import org.apache.gora.query.impl.PartitionQueryImpl;
import org.apache.gora.store.DataStoreFactory;
import org.apache.gora.store.impl.DataStoreBase;
import org.apache.gora.util.GoraException;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * DataStore for HBase. Thread safe.
 *
 */
public class HBaseStore<K, T extends PersistentBase> extends DataStoreBase<K, T> {

    public static final Logger LOG = LoggerFactory.getLogger(HBaseStore.class);

    public static final String PARSE_MAPPING_FILE_KEY = "gora.hbase.mapping.file";

    public static final String DEFAULT_MAPPING_FILE = "gora-hbase-mapping.xml";

    /**
     * Key at DataStore Properties (same as gora.properties) to hold a mapping loaded from memory, instead from filesystem like
     * the one at PARSE_MAPPING_FILE_KEY. If this key is present, the mapping is loaded from the value instead of gora-hbase-mapping.xml
     */
    public static final String XML_MAPPING_DEFINITION = "gora.mapping";

    private static final String SCANNER_CACHING_PROPERTIES_KEY = "scanner.caching";
    private static final int SCANNER_CACHING_PROPERTIES_DEFAULT = 0;

    private static final int PUTS_AND_DELETES_PUT_TS_OFFSET = 1;
    private static final int PUTS_AND_DELETES_DELETE_TS_OFFSET = 2;

    private volatile Admin admin;

    private volatile HBaseTableConnection table;

    private final boolean autoCreateSchema = true;

    private volatile HBaseMapping mapping;

    private HBaseFilterUtil<K, T> filterUtil;

    private int scannerCaching = SCANNER_CACHING_PROPERTIES_DEFAULT;

    /**
     * Default constructor
     */
    public HBaseStore() {//Empty Constrctor
    }

    /**
     * Initialize the data store by reading the credentials, setting the client's properties up and
     * reading the mapping file. Initialize is called when then the call to
     * {@link org.apache.gora.store.DataStoreFactory#createDataStore} is made.
     *
     * The mapping can be passed as a configuration parameter 'gora.mapping' or taken from
     * gora-hbase-mapping.xml (in this order).
     *
     * @param keyClass
     * @param persistentClass
     * @param properties
     */
    @Override
    public void initialize(Class<K> keyClass, Class<T> persistentClass, Properties properties)
            throws GoraException {
        super.initialize(keyClass, persistentClass, properties);

        try {
            this.conf = HBaseConfiguration.create(getConf());
            admin = ConnectionFactory.createConnection(getConf()).getAdmin();

            InputStream mappingInputStream;
            // If there is a mapping definition in the Properties, use it.
            if (properties.containsKey(XML_MAPPING_DEFINITION)) {
                if (LOG.isTraceEnabled())
                    LOG.trace(XML_MAPPING_DEFINITION + " = " + properties.getProperty(XML_MAPPING_DEFINITION));
                mappingInputStream = IOUtils.toInputStream(properties.getProperty(XML_MAPPING_DEFINITION),
                        (Charset) null);
            }
            // Otherwise use the configuration from de default file gora-hbase-mapping.xml or whatever
            // configured in the key "gora.hbase.mapping.file"
            else {
                mappingInputStream = getClass().getClassLoader()
                        .getResourceAsStream(getConf().get(PARSE_MAPPING_FILE_KEY, DEFAULT_MAPPING_FILE));
            }

            mapping = readMapping(mappingInputStream);
            filterUtil = new HBaseFilterUtil<>(this.conf);
        } catch (FileNotFoundException ex) {
            throw new GoraException(
                    "Mapping file '" + getConf().get(PARSE_MAPPING_FILE_KEY, DEFAULT_MAPPING_FILE) + "' not found.",
                    ex);
        } catch (Exception e) {
            throw new GoraException(e);
        }

        // Set scanner caching option
        try {
            this.setScannerCaching(Integer.valueOf(DataStoreFactory.findProperty(this.properties, this,
                    SCANNER_CACHING_PROPERTIES_KEY, String.valueOf(SCANNER_CACHING_PROPERTIES_DEFAULT))));
        } catch (NumberFormatException e) {
            LOG.info("Can not load {} from gora.properties. Setting to default value: {}.",
                    SCANNER_CACHING_PROPERTIES_KEY, SCANNER_CACHING_PROPERTIES_DEFAULT);
            this.setScannerCaching(SCANNER_CACHING_PROPERTIES_DEFAULT); // Default value if something is wrong
        }

        if (autoCreateSchema) {
            createSchema();
        }
        try {
            boolean autoflush = this.conf.getBoolean("hbase.client.autoflush.default", false);
            table = new HBaseTableConnection(getConf(), getSchemaName(), autoflush);
        } catch (Exception e) {
            throw new GoraException(e);
        }
        closeHBaseAdmin();
    }

    @Override
    public String getSchemaName() {
        //return the name of this table
        return mapping.getTableName();
    }

    public HBaseMapping getMapping() {
        return mapping;
    }

    @Override
    public void createSchema() throws GoraException {
        try {
            if (schemaExists()) {
                return;
            }
            TableDescriptor tableDesc = mapping.getTable();

            admin.createTable(tableDesc);
        } catch (GoraException e) {
            throw e;
        } catch (Exception e) {
            throw new GoraException(e);
        }
        closeHBaseAdmin();
    }

    @Override
    public void deleteSchema() throws GoraException {
        try {
            if (!schemaExists()) {
                return;
            }
            admin.disableTable(mapping.getTable().getTableName());
            admin.deleteTable(mapping.getTable().getTableName());
        } catch (GoraException e) {
            throw e;
        } catch (Exception e) {
            throw new GoraException(e);
        }
        closeHBaseAdmin();
    }

    @Override
    public boolean schemaExists() throws GoraException {
        try {
            return admin.tableExists(mapping.getTable().getTableName());
        } catch (Exception e) {
            throw new GoraException(e);
        }
    }

    @Override
    public T get(K key, String[] fields) throws GoraException {
        try {
            fields = getFieldsToQuery(fields);
            Get get = new Get(toBytes(key));
            addFields(get, fields);
            Result result = table.get(get);
            return newInstance(result, fields);
        } catch (GoraException e) {
            throw e;
        } catch (Exception e) {
            throw new GoraException(e);
        }
    }

    /**
     * {@inheritDoc} Serializes the Persistent data and saves in HBase. Topmost
     * fields of the record are persisted in "raw" format (not avro serialized).
     * This behavior happens in maps and arrays too.
     * 
     * ["null","type"] type (a.k.a. optional field) is persisted like as if it is
     * ["type"], but the column gets deleted if value==null (so value read after
     * will be null).
     * 
     * @param persistent
     *          Record to be persisted in HBase
     */
    @Override
    public void put(K key, T persistent) throws GoraException {
        try {
            Schema schema = persistent.getSchema();
            byte[] keyRaw = toBytes(key);
            long timeStamp = System.currentTimeMillis();
            // Guarantee Put after Delete
            Put put = new Put(keyRaw, timeStamp - PUTS_AND_DELETES_PUT_TS_OFFSET);
            Delete delete = new Delete(keyRaw, timeStamp - PUTS_AND_DELETES_DELETE_TS_OFFSET);

            List<Field> fields = schema.getFields();
            for (int i = 0; i < fields.size(); i++) {
                if (!persistent.isDirty(i)) {
                    continue;
                }
                Field field = fields.get(i);
                Object o = persistent.get(i);
                HBaseColumn hcol = mapping.getColumn(field.name());
                if (hcol == null) {
                    String errorMsg = "HBase mapping for field [" + persistent.getClass().getName() + "#"
                            + field.name() + "] not found. Wrong gora-hbase-mapping.xml?";
                    LOG.error(errorMsg);
                    throw new GoraException(errorMsg);
                }
                addPutsAndDeletes(put, delete, o, field.schema().getType(), field.schema(), hcol,
                        hcol.getQualifier());
            }

            if (delete.size() > 0) {
                table.delete(delete);
            }
            if (put.size() > 0) {
                table.put(put);
            }
        } catch (GoraException e) {
            throw e;
        } catch (Exception e) {
            throw new GoraException(e);
        }
    }

    private void addPutsAndDeletes(Put put, Delete delete, Object o, Type type, Schema schema, HBaseColumn hcol,
            byte[] qualifier) throws IOException {
        switch (type) {
        case UNION:
            if (isNullable(schema) && o == null) {
                if (qualifier == null) {
                    delete.addFamily(hcol.getFamily());
                } else {
                    delete.addColumns(hcol.getFamily(), qualifier);
                }
            } else {
                //        int index = GenericData.get().resolveUnion(schema, o);
                int index = getResolvedUnionIndex(schema);
                if (index > 1) { //if more than 2 type in union, serialize directly for now
                    byte[] serializedBytes = toBytes(o, schema);
                    put.addColumn(hcol.getFamily(), qualifier, serializedBytes);
                } else {
                    Schema resolvedSchema = schema.getTypes().get(index);
                    addPutsAndDeletes(put, delete, o, resolvedSchema.getType(), resolvedSchema, hcol, qualifier);
                }
            }
            break;
        case MAP:
            // if it's a map that has been modified, then the content should be replaced by the new one
            // This is because we don't know if the content has changed or not.
            if (qualifier == null) {
                delete.addFamily(hcol.getFamily());
            } else {
                delete.addColumns(hcol.getFamily(), qualifier);
            }
            @SuppressWarnings({ "rawtypes", "unchecked" })
            Set<Entry> set = ((Map) o).entrySet();
            for (@SuppressWarnings("rawtypes")
            Entry entry : set) {
                byte[] qual = toBytes(entry.getKey());
                addPutsAndDeletes(put, delete, entry.getValue(), schema.getValueType().getType(),
                        schema.getValueType(), hcol, qual);
            }
            break;
        case ARRAY:
            List<?> array = (List<?>) o;
            int j = 0;
            for (Object item : array) {
                addPutsAndDeletes(put, delete, item, schema.getElementType().getType(), schema.getElementType(),
                        hcol, Bytes.toBytes(j++));
            }
            break;
        default:
            byte[] serializedBytes = toBytes(o, schema);
            put.addColumn(hcol.getFamily(), qualifier, serializedBytes);
            break;
        }
    }

    private boolean isNullable(Schema unionSchema) {
        for (Schema innerSchema : unionSchema.getTypes()) {
            if (innerSchema.getType().equals(Schema.Type.NULL)) {
                return true;
            }
        }
        return false;
    }

    public void delete(T obj) {
        throw new RuntimeException("Not implemented yet");
    }

    /**
     * Deletes the object with the given key.
     * @return always true
     */
    @Override
    public boolean delete(K key) throws GoraException {
        try {
            table.delete(new Delete(toBytes(key)));
            //HBase does not return success information and executing a get for
            //success is a bit costly
            return true;
        } catch (Exception e) {
            throw new GoraException(e);
        }
    }

    @Override
    public long deleteByQuery(Query<K, T> query) throws GoraException {
        try {
            String[] fields = getFieldsToQuery(query.getFields());
            //find whether all fields are queried, which means that complete
            //rows will be deleted
            boolean isAllFields = Arrays.equals(fields, getFields());

            org.apache.gora.query.Result<K, T> result = null;
            result = query.execute();
            ArrayList<Delete> deletes = new ArrayList<>();
            while (result.next()) {
                Delete delete = new Delete(toBytes(result.getKey()));
                deletes.add(delete);
                if (!isAllFields) {
                    addFields(delete, query);
                }
            }
            table.delete(deletes);
            return deletes.size();
        } catch (GoraException e) {
            throw e;
        } catch (Exception e) {
            throw new GoraException(e);
        }
    }

    @Override
    public void flush() throws GoraException {
        try {
            table.flushCommits();
        } catch (Exception e) {
            throw new GoraException(e);
        }
    }

    @Override
    public Query<K, T> newQuery() {
        return new HBaseQuery<>(this);
    }

    @Override
    public List<PartitionQuery<K, T>> getPartitions(Query<K, T> query) throws IOException {

        if (table == null) {
            throw new IOException("No table was provided.");
        }

        // taken from o.a.h.hbase.mapreduce.TableInputFormatBase
        Pair<byte[][], byte[][]> keys = table.getStartEndKeys();
        if (keys == null || keys.getFirst() == null || keys.getFirst().length == 0) {
            throw new IOException("Expecting at least one region.");
        }

        List<PartitionQuery<K, T>> partitions = new ArrayList<>(keys.getFirst().length);
        for (int i = 0; i < keys.getFirst().length; i++) {
            String regionLocation = table.getRegionLocation(keys.getFirst()[i]).getHostname();
            byte[] startRow = query.getStartKey() != null ? toBytes(query.getStartKey())
                    : HConstants.EMPTY_START_ROW;
            byte[] stopRow = query.getEndKey() != null ? toBytes(query.getEndKey()) : HConstants.EMPTY_END_ROW;

            // determine if the given start an stop key fall into the region
            if ((startRow.length == 0 || keys.getSecond()[i].length == 0
                    || Bytes.compareTo(startRow, keys.getSecond()[i]) < 0)
                    && (stopRow.length == 0 || Bytes.compareTo(stopRow, keys.getFirst()[i]) > 0)) {

                byte[] splitStart = startRow.length == 0 || Bytes.compareTo(keys.getFirst()[i], startRow) >= 0
                        ? keys.getFirst()[i]
                        : startRow;

                byte[] splitStop = (stopRow.length == 0 || Bytes.compareTo(keys.getSecond()[i], stopRow) <= 0)
                        && keys.getSecond()[i].length > 0 ? keys.getSecond()[i] : stopRow;

                K startKey = Arrays.equals(HConstants.EMPTY_START_ROW, splitStart) ? null
                        : HBaseByteInterface.fromBytes(keyClass, splitStart);
                K endKey = Arrays.equals(HConstants.EMPTY_END_ROW, splitStop) ? null
                        : HBaseByteInterface.fromBytes(keyClass, splitStop);

                PartitionQueryImpl<K, T> partition = new PartitionQueryImpl<>(query, startKey, endKey,
                        regionLocation);
                partition.setConf(getConf());

                partitions.add(partition);
            }
        }
        return partitions;
    }

    @Override
    public org.apache.gora.query.Result<K, T> execute(Query<K, T> query) throws GoraException {
        try {
            //check if query.fields is null
            query.setFields(getFieldsToQuery(query.getFields()));

            if (query.getStartKey() != null && query.getStartKey().equals(query.getEndKey())) {
                Get get = new Get(toBytes(query.getStartKey()));
                addFields(get, query.getFields());
                addTimeRange(get, query);
                Result result = table.get(get);
                return new HBaseGetResult<>(this, query, result);
            } else {
                ResultScanner scanner = createScanner(query);

                org.apache.gora.query.Result<K, T> result = new HBaseScannerResult<>(this, query, scanner);

                return result;
            }
        } catch (IOException ex) {
            throw new GoraException(ex);
        }
    }

    public ResultScanner createScanner(Query<K, T> query) throws IOException {
        final Scan scan = new Scan();
        scan.setMaxResultSize(query.getLimit());
        scan.setCaching(this.getScannerCaching());

        if (query.getStartKey() != null) {
            scan.withStartRow(toBytes(query.getStartKey()));
        }
        if (query.getEndKey() != null) {
            // In HBase the end key is exclusive, so we make it inclusive by explicitly passing
            // boolean 'true' as the Gora's query interface declares.
            scan.withStopRow(toBytes(query.getEndKey()), true);
        }
        addFields(scan, query);
        if (query.getFilter() != null) {
            boolean succeeded = filterUtil.setFilter(scan, query.getFilter(), this);
            if (succeeded) {
                // don't need local filter
                query.setLocalFilterEnabled(false);
            }
        }

        return table.getScanner(scan);
    }

    private void addFields(Get get, String[] fieldNames) {
        for (String f : fieldNames) {
            HBaseColumn col = mapping.getColumn(f);
            if (col == null) {
                throw new RuntimeException(
                        "HBase mapping for field [" + f + "] not found. " + "Wrong gora-hbase-mapping.xml?");
            }
            Schema fieldSchema = fieldMap.get(f).schema();
            addFamilyOrColumn(get, col, fieldSchema);
        }
    }

    private void addFamilyOrColumn(Get get, HBaseColumn col, Schema fieldSchema) {
        switch (fieldSchema.getType()) {
        case UNION:
            int index = getResolvedUnionIndex(fieldSchema);
            Schema resolvedSchema = fieldSchema.getTypes().get(index);
            addFamilyOrColumn(get, col, resolvedSchema);
            break;
        case MAP:
        case ARRAY:
            get.addFamily(col.family);
            break;
        default:
            get.addColumn(col.family, col.qualifier);
            break;
        }
    }

    private void addFields(Scan scan, Query<K, T> query) throws IOException {
        String[] fields = query.getFields();
        for (String f : fields) {
            HBaseColumn col = mapping.getColumn(f);
            if (col == null) {
                throw new RuntimeException(
                        "HBase mapping for field [" + f + "] not found. " + "Wrong gora-hbase-mapping.xml?");
            }
            Schema fieldSchema = fieldMap.get(f).schema();
            addFamilyOrColumn(scan, col, fieldSchema);
        }
    }

    private void addFamilyOrColumn(Scan scan, HBaseColumn col, Schema fieldSchema) {
        switch (fieldSchema.getType()) {
        case UNION:
            int index = getResolvedUnionIndex(fieldSchema);
            Schema resolvedSchema = fieldSchema.getTypes().get(index);
            addFamilyOrColumn(scan, col, resolvedSchema);
            break;
        case MAP:
        case ARRAY:
            scan.addFamily(col.family);
            break;
        default:
            scan.addColumn(col.family, col.qualifier);
            break;
        }
    }

    // TODO: HBase Get, Scan, Delete should extend some common interface with
    // addFamily, etc
    private void addFields(Delete delete, Query<K, T> query) throws IOException {
        String[] fields = query.getFields();
        for (String f : fields) {
            HBaseColumn col = mapping.getColumn(f);
            if (col == null) {
                throw new RuntimeException(
                        "HBase mapping for field [" + f + "] not found. " + "Wrong gora-hbase-mapping.xml?");
            }
            Schema fieldSchema = fieldMap.get(f).schema();
            addFamilyOrColumn(delete, col, fieldSchema);
        }
    }

    private void addFamilyOrColumn(Delete delete, HBaseColumn col, Schema fieldSchema) {
        switch (fieldSchema.getType()) {
        case UNION:
            int index = getResolvedUnionIndex(fieldSchema);
            Schema resolvedSchema = fieldSchema.getTypes().get(index);
            addFamilyOrColumn(delete, col, resolvedSchema);
            break;
        case MAP:
        case ARRAY:
            delete.addFamily(col.family);
            break;
        default:
            delete.addColumns(col.family, col.qualifier);
            break;
        }
    }

    private void addTimeRange(Get get, Query<K, T> query) throws IOException {
        if (query.getStartTime() > 0 || query.getEndTime() > 0) {
            if (query.getStartTime() == query.getEndTime()) {
                get.setTimeStamp(query.getStartTime());
            } else {
                long startTime = query.getStartTime() > 0 ? query.getStartTime() : 0;
                long endTime = query.getEndTime() > 0 ? query.getEndTime() : Long.MAX_VALUE;
                get.setTimeRange(startTime, endTime);
            }
        }
    }

    /**
     * Creates a new Persistent instance with the values in 'result' for the fields listed.
     * @param result result form a HTable#get()
     * @param fields List of fields queried, or null for all
     * @return A new instance with default values for not listed fields
     *         null if 'result' is null.
     * @throws IOException
     */
    public T newInstance(Result result, String[] fields) throws IOException {
        if (result == null || result.isEmpty())
            return null;

        T persistent = newPersistent();
        for (String f : fields) {
            HBaseColumn col = mapping.getColumn(f);
            if (col == null) {
                throw new RuntimeException(
                        "HBase mapping for field [" + f + "] not found. " + "Wrong gora-hbase-mapping.xml?");
            }
            Field field = fieldMap.get(f);
            Schema fieldSchema = field.schema();
            setField(result, persistent, col, field, fieldSchema);
        }
        persistent.clearDirty();
        return persistent;
    }

    private void setField(Result result, T persistent, HBaseColumn col, Field field, Schema fieldSchema)
            throws IOException {
        switch (fieldSchema.getType()) {
        case UNION:
            int index = getResolvedUnionIndex(fieldSchema);
            if (index > 1) { //if more than 2 type in union, deserialize directly for now
                byte[] val = result.getValue(col.getFamily(), col.getQualifier());
                if (val == null) {
                    return;
                }
                setField(persistent, field, val);
            } else {
                Schema resolvedSchema = fieldSchema.getTypes().get(index);
                setField(result, persistent, col, field, resolvedSchema);
            }
            break;
        case MAP:
            NavigableMap<byte[], byte[]> qualMap = result.getNoVersionMap().get(col.getFamily());
            if (qualMap == null) {
                return;
            }
            Schema valueSchema = fieldSchema.getValueType();
            Map<Utf8, Object> map = new HashMap<>();
            for (Entry<byte[], byte[]> e : qualMap.entrySet()) {
                map.put(new Utf8(Bytes.toString(e.getKey())), fromBytes(valueSchema, e.getValue()));
            }
            setField(persistent, field, map);
            break;
        case ARRAY:
            qualMap = result.getFamilyMap(col.getFamily());
            if (qualMap == null) {
                return;
            }
            valueSchema = fieldSchema.getElementType();
            ArrayList<Object> arrayList = new ArrayList<>();
            DirtyListWrapper<Object> dirtyListWrapper = new DirtyListWrapper<>(arrayList);
            for (Entry<byte[], byte[]> e : qualMap.entrySet()) {
                dirtyListWrapper.add(fromBytes(valueSchema, e.getValue()));
            }
            setField(persistent, field, arrayList);
            break;
        default:
            byte[] val = result.getValue(col.getFamily(), col.getQualifier());
            if (val == null) {
                return;
            }
            setField(persistent, field, val);
            break;
        }
    }

    //TODO temporary solution, has to be changed after implementation of saving the index of union type
    private int getResolvedUnionIndex(Schema unionScema) {
        if (unionScema.getTypes().size() == 2) {

            // schema [type0, type1]
            Type type0 = unionScema.getTypes().get(0).getType();
            Type type1 = unionScema.getTypes().get(1).getType();

            // Check if types are different and there's a "null", like ["null","type"]
            // or ["type","null"]
            if (!type0.equals(type1) && (type0.equals(Schema.Type.NULL) || type1.equals(Schema.Type.NULL))) {

                if (type0.equals(Schema.Type.NULL))
                    return 1;
                else
                    return 0;
            }
        }
        return 2;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void setField(T persistent, Field field, Map map) {
        persistent.put(field.pos(), new DirtyMapWrapper(map));
    }

    private void setField(T persistent, Field field, byte[] val) throws IOException {
        persistent.put(field.pos(), fromBytes(field.schema(), val));
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void setField(T persistent, Field field, List list) {
        persistent.put(field.pos(), new DirtyListWrapper(list));
    }

    @SuppressWarnings("unchecked")
    private HBaseMapping readMapping(InputStream mappingStream) throws IOException {

        HBaseMappingBuilder mappingBuilder = new HBaseMappingBuilder();

        try {
            SAXBuilder builder = new SAXBuilder();
            Document doc = builder.build(mappingStream);
            Element root = doc.getRootElement();

            List<Element> tableElements = root.getChildren("table");
            for (Element tableElement : tableElements) {
                String tableName = tableElement.getAttributeValue("name");

                List<Element> fieldElements = tableElement.getChildren("family");
                for (Element fieldElement : fieldElements) {
                    String familyName = fieldElement.getAttributeValue("name");
                    String compression = fieldElement.getAttributeValue("compression");
                    String blockCache = fieldElement.getAttributeValue("blockCache");
                    String blockSize = fieldElement.getAttributeValue("blockSize");
                    String bloomFilter = fieldElement.getAttributeValue("bloomFilter");
                    String maxVersions = fieldElement.getAttributeValue("maxVersions");
                    String timeToLive = fieldElement.getAttributeValue("timeToLive");
                    String inMemory = fieldElement.getAttributeValue("inMemory");

                    mappingBuilder.addFamilyProps(tableName, familyName, compression, blockCache, blockSize,
                            bloomFilter, maxVersions, timeToLive, inMemory);
                }
            }

            List<Element> classElements = root.getChildren("class");
            boolean keyClassMatches = false;
            for (Element classElement : classElements) {
                if (classElement.getAttributeValue("keyClass").equals(keyClass.getCanonicalName())
                        && classElement.getAttributeValue("name").equals(persistentClass.getCanonicalName())) {
                    LOG.debug("Keyclass and nameclass match.");
                    keyClassMatches = true;

                    String tableNameFromMapping = classElement.getAttributeValue("table");
                    String tableName = getSchemaName(tableNameFromMapping, persistentClass);

                    //tableNameFromMapping could be null here
                    if (!tableName.equals(tableNameFromMapping)) {
                        //TODO this might not be the desired behavior as the user might have actually made a mistake.
                        LOG.warn(
                                "Mismatching schema's names. Mappingfile schema: '{}'. PersistentClass schema's name: '{}'. Assuming they are the same.",
                                tableNameFromMapping, tableName);
                        if (tableNameFromMapping != null) {
                            mappingBuilder.renameTable(tableNameFromMapping, tableName);
                        }
                    }
                    mappingBuilder.setTableName(tableName);

                    List<Element> fields = classElement.getChildren("field");
                    for (Element field : fields) {
                        String fieldName = field.getAttributeValue("name");
                        String family = field.getAttributeValue("family");
                        String qualifier = field.getAttributeValue("qualifier");
                        mappingBuilder.addField(fieldName, family, qualifier);
                        mappingBuilder.addColumnFamily(tableName, family);
                    }
                    //we found a matching key and value class definition,
                    //do not continue on other class definitions
                    break;
                }
            }
            if (!keyClassMatches) {
                throw new ConfigurationException(
                        "Gora-hbase-mapping does not include the name and keyClass in the databean.");
            }
        } catch (MalformedURLException ex) {
            LOG.error("Error while trying to read the mapping file {}. " + "Expected to be in the classpath "
                    + "(ClassLoader#getResource(java.lang.String)).", mappingStream);
            LOG.error("Actual classpath = {}",
                    Arrays.asList(((URLClassLoader) getClass().getClassLoader()).getURLs()));
            throw ex;
        } catch (IOException ex) {
            LOG.error(ex.getMessage(), ex);
            throw ex;
        } catch (Exception ex) {
            LOG.error(ex.getMessage(), ex);
            throw new IOException(ex);
        }

        return mappingBuilder.build();
    }

    @Override
    public void close() {
        try {
            table.close();
        } catch (IOException ex) {
            LOG.error(ex.getMessage(), ex);
        }
    }

    /**
     * Gets the Scanner Caching optimization value
     * @return The value used internally in {@link Scan#setCaching(int)}
     */
    public int getScannerCaching() {
        return this.scannerCaching;
    }

    /**
     * Sets the value for Scanner Caching optimization
     * 
     * @see Scan#setCaching(int)
     * 
     * @param numRows the number of rows for caching {@literal >=} 0
     * @return &lt;&lt;Fluent interface&gt;&gt;
     */
    public HBaseStore<K, T> setScannerCaching(int numRows) {
        if (numRows < 0) {
            LOG.warn("Invalid Scanner Caching optimization value. Cannot set to: {}.", numRows);
            return this;
        }
        this.scannerCaching = numRows;
        return this;
    }

    private void closeHBaseAdmin() {
        try {
            admin.close();
        } catch (IOException ioe) {
            LOG.error("An error occured whilst closing HBase Admin", ioe);
        }
    }
}