org.apache.accumulo.test.merkle.MerkleTreeNode.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.accumulo.test.merkle.MerkleTreeNode.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.accumulo.test.merkle;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.Value;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Encapsulates the level (height) within the tree, the ranges that it covers, and the new hash 
 */
public class MerkleTreeNode {
    private static final Logger log = LoggerFactory.getLogger(MerkleTreeNode.class);

    private Range range;
    private int level;
    private List<Range> children;
    private byte[] hash;

    public MerkleTreeNode(Range range, int level, List<Range> children, byte[] hash) {
        this.range = range;
        this.level = level;
        this.children = children;
        this.hash = hash;
    }

    public MerkleTreeNode(Key k, Value v) {
        range = RangeSerialization.toRange(k);
        level = 0;
        children = Collections.emptyList();
        hash = v.get();
    }

    public MerkleTreeNode(List<MerkleTreeNode> children, String digestAlgorithm) throws NoSuchAlgorithmException {
        level = 0;
        this.children = new ArrayList<Range>(children.size());
        MessageDigest digest = MessageDigest.getInstance(digestAlgorithm);

        Range childrenRange = null;
        for (MerkleTreeNode child : children) {
            this.children.add(child.getRange());
            level = Math.max(child.getLevel(), level);
            digest.update(child.getHash());

            if (null == childrenRange) {
                childrenRange = child.getRange();
            } else {
                List<Range> overlappingRanges = Range
                        .mergeOverlapping(Arrays.asList(childrenRange, child.getRange()));
                if (1 != overlappingRanges.size()) {
                    log.error("Tried to merge non-contiguous ranges: {} {}", childrenRange, child.getRange());
                    throw new IllegalArgumentException(
                            "Ranges must be contiguous: " + childrenRange + ", " + child.getRange());
                }

                childrenRange = overlappingRanges.get(0);
            }
        }

        // Our actual level is one more than the highest level of our children
        level++;

        // Roll the hash up the tree
        hash = digest.digest();

        // Set the range to be the merged result of the children
        range = childrenRange;
    }

    public Range getRange() {
        return range;
    }

    public int getLevel() {
        return level;
    }

    public List<Range> getChildren() {
        return children;
    }

    public byte[] getHash() {
        return hash;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(32);
        sb.append("range=").append(range).append(" level=").append(level).append(" hash=")
                .append(Hex.encodeHexString(hash)).append(" children=").append(children);
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof MerkleTreeNode) {
            MerkleTreeNode other = (MerkleTreeNode) o;
            return range.equals(other.getRange()) && level == other.getLevel()
                    && children.equals(other.getChildren()) && Arrays.equals(hash, other.getHash());
        }

        return false;
    }

    @Override
    public int hashCode() {
        HashCodeBuilder hcb = new HashCodeBuilder(1395, 39532);
        return hcb.append(range).append(level).append(children).append(hash).toHashCode();
    }
}