org.cloudgraph.hbase.io.DefaultEdgeOperation.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudgraph.hbase.io.DefaultEdgeOperation.java

Source

/**
 * Copyright 2017 TerraMeta Software, Inc.
 * 
 * Licensed 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.cloudgraph.hbase.io;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.cloudgraph.state.StateException;
import org.cloudgraph.state.proto.RowKeysProto.RowKey;
import org.cloudgraph.state.proto.RowKeysProto.RowKeys;
import org.cloudgraph.store.key.RequiredKeyFieldException;
import org.cloudgraph.store.mapping.DataGraphMapping;
import org.cloudgraph.store.mapping.StoreMapping;
import org.cloudgraph.store.mapping.TableMapping;
import org.plasma.sdo.PlasmaProperty;
import org.plasma.sdo.PlasmaType;
import org.plasma.sdo.helper.PlasmaTypeHelper;

import com.google.protobuf.ByteString;

public abstract class DefaultEdgeOperation implements EdgeOperation {
    private static Log log = LogFactory.getLog(DefaultEdgeOperation.class);

    /** internal delimiter for metadta values */
    protected static String TYPE_DELIM = "|";
    protected static String TYPE_DELIM_REGEXP = "\\|";

    protected PlasmaType sourceType;
    protected PlasmaProperty sourceProp;
    protected TableMapping tableConfig;
    protected DataGraphMapping graphConfig;
    protected static Charset charset = Charset.forName("UTF-8");

    protected PlasmaType collectionBaseType;
    protected PlasmaType collectionDefaultSubType;
    protected String collectionPath;
    protected Set<Long> sequences;
    // protected String table;
    protected List<KeyBytes> rowKeys;

    protected byte[] family;
    protected byte[] columnKeyFeildDelim;
    protected String columnKeyFeildDelimRegexp;

    protected byte[] baseTypeQual;
    protected byte[] subTypeQual;
    protected byte[] pathQual;
    protected byte[] sequencesQual;
    // protected byte[] tableQual;
    protected byte[] keysQual;
    protected byte[] countQual;

    protected String typeQualStr;
    protected String pathQualStr;
    protected String sequencesQualStr;
    // protected String tableQualStr;
    protected String keysQualStr;
    protected String countQualStr;

    protected DefaultEdgeOperation(PlasmaType sourceType, PlasmaProperty sourceProp, TableMapping tableConfig,
            DataGraphMapping graphConfig) throws IOException {
        super();
        this.sourceType = sourceType;
        this.sourceProp = sourceProp;
        this.tableConfig = tableConfig;
        this.graphConfig = graphConfig;
        charset = tableConfig.getCharset();

        this.family = tableConfig.getDataColumnFamilyNameBytes();

        this.columnKeyFeildDelim = graphConfig.getColumnKeyFieldDelimiterBytes();
        this.columnKeyFeildDelimRegexp = graphConfig.getColumnKeyFieldDelimiter();
        if (this.columnKeyFeildDelimRegexp.length() == 1) {
            char c = this.columnKeyFeildDelimRegexp.charAt(0);
            if (isSpecial(c)) {
                columnKeyFeildDelimRegexp = "\\" + String.valueOf(c);
            }
        }
    }

    public PlasmaType getTargetType() {
        return sourceType;
    }

    public PlasmaProperty getSourceProp() {
        return sourceProp;
    }

    private boolean isSpecial(char c) {
        switch (c) {
        case '|':
        case '*':
            return true;
        default:
            return false;
        }
    }

    /**
     * Calculates and returns whether the edges are local. If the opposite type is
     * not bound (to a table), the type is stored in the local row and we write
     * its sequences for this edge. Or if this type is not bound to a table, we
     * also know its opposite edges must be local as well.
     * 
     * @return whether the edges are local
     */
    // protected boolean localEdges() {
    // //return !this.oppositeTypeBound || !this.thisTypeBound;
    // return !this.oppositeTypeBound;
    // }

    /*
     * (non-Javadoc)
     * 
     * @see org.cloudgraph.hbase.io.KeyMeta#getBaseType()
     */
    @Override
    public PlasmaType getBaseType() {
        return collectionBaseType;
    }

    @Override
    public PlasmaType getSubType() {
        return this.collectionDefaultSubType;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.cloudgraph.hbase.io.KeyMeta#getCollectionPath()
     */
    @Override
    public String getPath() {
        return collectionPath;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.cloudgraph.hbase.io.KeyMeta#getSequences()
     */
    @Override
    public Set<Long> getSequences() {
        return sequences;
    }

    @Override
    public boolean hasSequence(Long seq) {
        if (this.sequences != null)
            return this.sequences.contains(seq);
        else
            return false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.cloudgraph.hbase.io.KeyMeta#getTable()
     */
    @Override
    public String getTable() {
        if (!isExternal())
            throw new IllegalStateException("cannot find table for local (non-external) edge, " + this);

        TableMapping table = null;
        if (this.collectionDefaultSubType != null) {
            table = StoreMapping.getInstance().findTable(this.collectionDefaultSubType.getQualifiedName());
        } else {
            table = StoreMapping.getInstance().findTable(this.collectionBaseType.getQualifiedName());
        }
        return table.getName();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.cloudgraph.hbase.io.KeyMeta#getRowKeys()
     */
    @Override
    public List<KeyBytes> getRowKeys() {
        return rowKeys;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.cloudgraph.hbase.io.KeyMeta#isExternal()
     */
    private Boolean edgeTypeBound;

    @Override
    public boolean isExternal() {
        if (edgeTypeBound != null) {
            return edgeTypeBound;
        } else {
            boolean result = false;
            if (this.collectionDefaultSubType != null) {
                result = StoreMapping.getInstance()
                        .findTable(this.collectionDefaultSubType.getQualifiedName()) != null;
            } else {
                edgeTypeBound = StoreMapping.getInstance()
                        .findTable(this.collectionBaseType.getQualifiedName()) != null;
                result = edgeTypeBound;
            }
            return result;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.cloudgraph.hbase.io.KeyMeta#getCount()
     */
    @Override
    public long getCount() {
        if (this.sequences != null && this.sequences.size() > 0)
            return this.sequences.size();
        else if (this.rowKeys != null && this.rowKeys.size() > 0)
            return this.rowKeys.size();
        else
            return 0;
    }

    protected byte[] encodeRowKeys() {
        RowKeys.Builder rowKeysBuilder = RowKeys.newBuilder();
        for (KeyBytes keyBytes : rowKeys) {
            RowKey.Builder rowKeyBuilder = RowKey.newBuilder();
            rowKeyBuilder.setKey(ByteString.copyFrom(keyBytes.getKey()));
            rowKeysBuilder.addRowKey(rowKeyBuilder);
        }
        RowKeys keys = rowKeysBuilder.build();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] bytes = null;
        try {
            keys.writeDelimitedTo(baos);
            baos.flush();
            bytes = baos.toByteArray();
        } catch (IOException e) {
            throw new StateException(e);
        } finally {
            try {
                baos.close();
            } catch (IOException e) {
            }
        }
        return bytes;
    }

    protected void decodeRowKeys(byte[] rowKeys) {
        ByteArrayInputStream bais = new ByteArrayInputStream(rowKeys);
        try {
            RowKeys.Builder result = RowKeys.newBuilder();
            result.mergeDelimitedFrom(bais);
            RowKeys keys = result.build();
            this.rowKeys = new ArrayList<>(keys.getRowKeyCount());
            for (RowKey rowKey : keys.getRowKeyList())
                this.rowKeys.add(new KeyBytes(rowKey.getKey().toByteArray()));
        } catch (IOException e) {
            throw new StateException(e);
        } finally {
            try {
                bais.close();
            } catch (IOException e) {
            }
        }
    }

    protected byte[] encodeSequences() {
        StringBuilder buf = new StringBuilder();
        int i = 0;
        for (Long seq : sequences) {
            if (i > 0)
                buf.append(' ');
            buf.append(String.valueOf(seq));
            i++;
        }
        return buf.toString().getBytes(charset);
    }

    protected void decodeSequences(byte[] sequenceBytes) {
        String[] tokens = (new String(sequenceBytes, charset)).split(" ");
        sequences = new HashSet<Long>(tokens.length);
        for (int i = 0; i < tokens.length; i++)
            sequences.add(Long.parseLong(tokens[i]));
    }

    // protected byte[] encodeSubType() {
    // //FIXME: pre-allocate capacity according to collection
    // ByteBuffer buf = ByteBuffer.allocate(6000);
    // Iterator<Long> iter = this.subTypeMap.keySet().iterator();
    // int i = 0;
    // while (iter.hasNext()) {
    // if (i > 0)
    // buf.put(" ".getBytes(charset));
    // Long seq = iter.next();
    // PlasmaType type = this.subTypeMap.get(seq);
    // byte[] typeBytes = encodeType(type);
    // byte[] subtypeBytes =
    // org.cloudgraph.common.Bytes.concat(String.valueOf(seq).getBytes(charset),
    // TYPE_DELIM.getBytes(charset), typeBytes);
    // buf.put(subtypeBytes);
    // i++;
    // }
    // byte[] result = new byte[buf.position()];
    // System.arraycopy(buf.array(), buf.arrayOffset(), result, 0,
    // buf.position());
    // return result;
    // }
    //
    // protected void decodeSubType(byte[] sequenceBytes) {
    // String[] elements = (new String(sequenceBytes, charset)).split(" ");
    // this.subTypeMap = new HashMap<>();
    // for (int i = 0; i < elements.length; i++) {
    // String[] tokens = elements[i].split(TYPE_DELIM_REGEXP);
    // Long seq = Long.parseLong(tokens[0]);
    // PlasmaType type =
    // (PlasmaType)PlasmaTypeHelper.INSTANCE.getType(tokens[1], tokens[2]);
    // this.subTypeMap.put(seq, type);
    // }
    // }

    protected byte[] encodeDefaultSubType() {
        return encodeType(this.collectionDefaultSubType);
    }

    protected void decodeDefaultSubType(byte[] typeBytes) {
        String[] tokens = (new String(typeBytes, this.charset)).split(this.columnKeyFeildDelimRegexp);
        this.collectionDefaultSubType = (PlasmaType) PlasmaTypeHelper.INSTANCE.getType(tokens[0], tokens[1]);
    }

    protected byte[] encodeCollectionBaseType() {
        return encodeType(this.collectionBaseType);
    }

    protected void decodeCollectionBaseType(byte[] typeBytes) {
        String[] tokens = (new String(typeBytes, this.charset)).split(this.columnKeyFeildDelimRegexp);
        this.collectionBaseType = (PlasmaType) PlasmaTypeHelper.INSTANCE.getType(tokens[0], tokens[1]);
    }

    public static byte[] encodeType(PlasmaType type) {
        byte[] uriPhysicalName = type.getURIPhysicalNameBytes();
        byte[] uri = type.getURIBytes();
        byte[] physicalName = type.getPhysicalNameBytes();
        byte[] name = type.getNameBytes();
        if (uriPhysicalName != null && physicalName != null) {
            return org.cloudgraph.common.Bytes.concat(uriPhysicalName, TYPE_DELIM.getBytes(charset), physicalName);
        } else {
            log.warn("no qualified physical name available for type, " + type
                    + ", encoding qualified logical name - please annotate your model with physical name aliases to facilitate logical/physical name isolation");
            return org.cloudgraph.common.Bytes.concat(uri, TYPE_DELIM.getBytes(charset), name);
        }
    }

    public static PlasmaType decodeType(byte[] typeBytes) {
        String[] tokens = (new String(typeBytes, charset)).split(TYPE_DELIM_REGEXP);
        PlasmaType result = (PlasmaType) PlasmaTypeHelper.INSTANCE.getType(tokens[0], tokens[1]);
        return result;
    }

    private byte[] concat(byte[]... fields) {
        return org.cloudgraph.common.Bytes.concat(fields);
    }

    protected byte[] fetchValue(byte[] family, byte[] qualifier, CellValues row) {
        if (!row.containsColumn(family, qualifier)) {
            throw new RequiredKeyFieldException("expected column qualifier '" + new String(qualifier)
                    + "' for reference property, " + sourceProp);
        }

        return row.getColumnValue(family, qualifier);
    }

    protected byte[] findValue(byte[] family, byte[] qualifier, CellValues row) {
        if (!row.containsColumn(family, qualifier)) {
            return null;
        }

        return row.getColumnValue(family, qualifier);
    }

    protected byte[] fetchValue(byte[] family, byte[] qualifier, Result row) {
        if (!row.containsColumn(family, qualifier)) {
            throw new RequiredKeyFieldException("expected column qualifier '" + new String(qualifier)
                    + "' for reference property, " + sourceProp);
        }

        return row.getValue(family, qualifier);
    }

    protected byte[] findValue(byte[] family, byte[] qualifier, Result row) {
        if (!row.containsColumn(family, qualifier)) {
            return null;
        }

        return row.getValue(family, qualifier);
    }

    protected byte[] fetchValue(byte[] family, byte[] qualifier, Put row) {
        if (!row.has(family, qualifier)) {
            throw new RequiredKeyFieldException("expected column qualifier '" + new String(qualifier)
                    + "' for reference property, " + sourceProp);
        }
        List<Cell> cells = row.get(family, qualifier);
        Cell last = cells.get(cells.size() - 1);
        return CellUtil.cloneValue(last);
    }

    protected byte[] findValue(byte[] family, byte[] qualifier, Put row) {
        if (!row.has(family, qualifier)) {
            return null;
        }
        List<Cell> cells = row.get(family, qualifier);
        Cell last = cells.get(cells.size() - 1);
        return CellUtil.cloneValue(last);
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(this.sourceProp);
        return buf.toString();
    }

    public class KeyBytes {
        private byte[] key;
        private int hashCode;

        public KeyBytes(byte[] key) {
            super();
            this.key = key;
            this.hashCode = Arrays.hashCode(this.key);
        }

        @Override
        public int hashCode() {
            return this.hashCode;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            KeyBytes other = (KeyBytes) obj;
            if (hashCode != other.hashCode)
                return false;
            return true;
        }

        public byte[] getKey() {
            return key;
        }

    }
}