org.lable.rfc3881.auditlogger.adapter.hbase.HBaseAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.lable.rfc3881.auditlogger.adapter.hbase.HBaseAdapter.java

Source

/*
 * Copyright (C) ${project.inceptionYear} Lable (info@lable.nl)
 *
 * 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.lable.rfc3881.auditlogger.adapter.hbase;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.util.Bytes;
import org.lable.codesystem.codereference.CodeReference;
import org.lable.codesystem.codereference.Identifiable;
import org.lable.codesystem.codereference.Referenceable;
import org.lable.oss.bitsandbytes.ByteMangler;
import org.lable.rfc3881.auditlogger.api.AuditLogAdapter;
import org.lable.rfc3881.auditlogger.api.Event;
import org.lable.rfc3881.auditlogger.api.LogEntry;
import org.lable.rfc3881.auditlogger.serialization.ObjectMapperFactory;

import javax.inject.Inject;
import javax.inject.Named;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;

import static org.apache.hadoop.hbase.util.Bytes.toBytes;
import static org.lable.oss.bitsandbytes.ByteMangler.flipTheFirstBit;

/**
 * Persist audit log messages in a HBase table.
 */
public class HBaseAdapter implements AuditLogAdapter {
    static final byte[] NULL_BYTE = new byte[] { 0x00 };
    static final ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();

    final Connection connection;
    final TableName table;
    final byte[] columnFamily;

    @Inject
    public HBaseAdapter(Connection connection, @Named("audit-table") String tableName,
            @Named("audit-column-family") String columnFamily) {
        this.connection = connection;
        this.table = TableName.valueOf(tableName);
        this.columnFamily = toBytes(columnFamily);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void record(LogEntry logEntry) throws IOException {
        if (logEntry == null)
            return;

        // Very simple and naive approach for this first iteration.
        try (Table auditTable = connection.getTable(table)) {
            Put put = new Put(rowKeyFor(logEntry));
            addIfNotNull(put, "event", logEntry.getEvent());
            addIfNotNull(put, "requestor", logEntry.getRequestor());
            addIfNotNull(put, "delegator", logEntry.getDelegator());
            addIfNotNull(put, "access_point", logEntry.getNetworkAccessPoint());
            addIfNotNull(put, "principal", logEntry.getParticipatingPrincipals());
            addIfNotNull(put, "source", logEntry.getAuditSources());
            addIfNotNull(put, "object", logEntry.getParticipantObjects());
            addIfNotNull(put, "version", logEntry.getVersion());
            auditTable.put(put);
        }
    }

    void addIfNotNull(Put put, String qualifier, Object value) throws JsonProcessingException {
        addIfNotNull(put, toBytes(qualifier), value);
    }

    void addIfNotNull(Put put, String qualifier, Collection<? extends Identifiable> collection)
            throws JsonProcessingException {
        if (collection == null)
            return;
        for (Object value : collection) {
            addIfNotNull(put, toBytes(qualifier), value);
        }
    }

    void addIfNotNull(Put put, byte[] qualifier, Object value) throws JsonProcessingException {
        if (value == null)
            return;
        if (value instanceof Identifiable) {
            // Add the identifiers to the column qualifier.
            qualifier = Bytes.add(qualifier, NULL_BYTE, columnQualifierSuffixFor((Identifiable) value));
        }
        put.addColumn(columnFamily, qualifier, objectMapper.writeValueAsBytes(value));
    }

    static byte[] columnQualifierSuffixFor(Identifiable identifiable) {
        List<String> parts = identifiable.identifyingStack();
        // Account for the separator bytes.
        int targetLength = parts.size() - 1;
        for (String part : parts) {
            if (part != null) {
                targetLength += part.length();
            }
        }

        ByteBuffer buffer = ByteBuffer.allocate(targetLength);
        boolean first = true;
        for (String part : parts) {
            if (!first) {
                buffer.put(NULL_BYTE);
            } else {
                first = false;
            }

            if (part != null) {
                buffer.put(toBytes(part));
            }
        }

        return buffer.array();
    }

    static byte[] rowKeyFor(LogEntry logEntry) {
        Event event = logEntry.getEvent();
        return Bytes.add(
                // Flip the bytes in the data to order descending; latest event first.
                ByteMangler.flip(flipTheFirstBit(toBytes(event.getHappenedAt().getMillis()))),
                referenceableToBytes(event.getId()));
    }

    static byte[] referenceableToBytes(Referenceable referenceable) {
        CodeReference codeReference = referenceable.toCodeReference();
        return Bytes.add(toBytes(codeReference.getCodeSystem()), NULL_BYTE, toBytes(codeReference.getCode()));
    }
}