All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.v5analytics.simpleorm.AccumuloSimpleOrmSession Maven / Gradle / Ivy

There is a newer version: 1.3.1
Show newest version
package com.v5analytics.simpleorm;

import org.apache.accumulo.core.client.*;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.iterators.user.RowDeletingIterator;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.core.security.ColumnVisibility;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.io.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static com.google.common.base.Preconditions.checkNotNull;

public class AccumuloSimpleOrmSession extends SimpleOrmSession {
    private static final Logger LOGGER = LoggerFactory.getLogger(AccumuloSimpleOrmSession.class);
    public static final String ACCUMULO_INSTANCE_NAME = "simpleOrm.accumulo.instanceName";
    public static final String ACCUMULO_USER = "simpleOrm.accumulo.username";
    public static final String ACCUMULO_PASSWORD = "simpleOrm.accumulo.password";
    public static final String ZK_SERVER_NAMES = "simpleOrm.accumulo.zookeeperServerNames";
    private static final String ROW_DELETING_ITERATOR_NAME = RowDeletingIterator.class.getSimpleName();
    private static final int ROW_DELETING_ITERATOR_PRIORITY = 7;
    private Connector connector;
    private BatchWriterConfig batchWriterConfig = new BatchWriterConfig();
    private final Set initializedTables = new HashSet<>();
    private final Map batchWriters = new HashMap<>();
    private String tablePrefix;

    public AccumuloSimpleOrmSession() {

    }

    public AccumuloSimpleOrmSession(Map properties) {
        setTablePrefix(properties);
    }

    public void init(Map properties) {
        try {
            String zkServerNames;
            if (properties.get(ZK_SERVER_NAMES) instanceof Collection) {
                zkServerNames = StringUtils.join((Collection) properties.get(ZK_SERVER_NAMES), ",");
            } else {
                zkServerNames = (String) properties.get(ZK_SERVER_NAMES);
            }
            checkNotNull(zkServerNames, "Could not find config: " + ZK_SERVER_NAMES);

            String accumuloInstanceName = (String) properties.get(ACCUMULO_INSTANCE_NAME);
            checkNotNull(accumuloInstanceName, "Could not find config: " + ACCUMULO_INSTANCE_NAME);
            ZooKeeperInstance zk = new ZooKeeperInstance(accumuloInstanceName, zkServerNames);

            String username = (String) properties.get(ACCUMULO_USER);
            String password = (String) properties.get(ACCUMULO_PASSWORD);
            connector = zk.getConnector(username, new PasswordToken(password));

            setTablePrefix(properties);
        } catch (Exception e) {
            throw new SimpleOrmException("Failed to init", e);
        }
    }

    @Override
    public SimpleOrmContext createContext(String... authorizations) {
        return new AccumuloSimpleOrmContext(new Authorizations(authorizations));
    }

    @Override
    public void close() {
        for (BatchWriter writer : this.batchWriters.values()) {
            try {
                writer.close();
            } catch (MutationsRejectedException e) {
                throw new SimpleOrmException("Could not close batch writer", e);
            }
        }
    }

    private void setTablePrefix(Map properties) {
        tablePrefix = (String) properties.get(TABLE_PREFIX);
        if (tablePrefix == null) {
            tablePrefix = "";
        }
    }

    @Override
    public  Iterable findAll(final Class rowClass, SimpleOrmContext context) {
        try {
            ModelMetadata modelMetadata = ModelMetadata.getModelMetadata(rowClass);
            final Scanner scanner = createScanner(getTableName(modelMetadata), (AccumuloSimpleOrmContext) context);
            return scannerToRows(scanner, modelMetadata);
        } catch (TableNotFoundException e) {
            throw new SimpleOrmException("Could not find all", e);
        }
    }

    @Override
    public  void delete(final Class rowClass, String id, SimpleOrmContext context) {
        ModelMetadata modelMetadata = ModelMetadata.getModelMetadata(rowClass);
        LOGGER.trace("deleteRow called with parameters: tableName=?, id=?", getTableName(modelMetadata), id);
        // In most instances (e.g., when reading is not necessary), the
        // RowDeletingIterator gives better performance than the deleting
        // mutation. This is due to the fact that Deleting mutations marks each
        // entry with a delete marker. Using the iterator marks a whole row with
        // a single mutation.
        try {
            BatchWriter writer = connector.createBatchWriter(getTableName(modelMetadata), batchWriterConfig);
            try {
                Mutation mutation = new Mutation(id);
                mutation.put(new byte[0], new byte[0], RowDeletingIterator.DELETE_ROW_VALUE.get());
                writer.addMutation(mutation);
                writer.flush();
            } catch (AccumuloException ae) {
                throw new SimpleOrmException("Could not delete", ae);
            } finally {
                writer.close();
            }
        } catch (Exception mre) {
            throw new SimpleOrmException("Could not delete: " + rowClass.getName() + " id " + id, mre);
        }
    }

    @Override
    public String getTablePrefix() {
        return tablePrefix;
    }

    @Override
    public Iterable getTableList(SimpleOrmContext simpleOrmContext) {
        return this.connector.tableOperations().list();
    }

    @Override
    public void deleteTable(String table, SimpleOrmContext simpleOrmContext) {
        try {
            this.connector.tableOperations().delete(table);
        } catch (Exception e) {
            throw new SimpleOrmException("Could not delete table: " + table, e);
        }
    }

    @Override
    public void clearTable(String table, SimpleOrmContext simpleOrmContext) {
        try {
            this.connector.tableOperations().deleteRows(table, null, null);
        } catch (Exception e) {
            throw new SimpleOrmException("Could not clear table: " + table, e);
        }
    }

    private  Iterable scannerToRows(final Scanner scanner, final ModelMetadata modelMetadata) {
        return new Iterable() {
            @Override
            public Iterator iterator() {
                final RowIterator rowIterator = new RowIterator(scanner);
                return new Iterator() {
                    @Override
                    public boolean hasNext() {
                        return rowIterator.hasNext();
                    }

                    @Override
                    public T next() {
                        Iterator> row = rowIterator.next();
                        return createObjectFromRow(modelMetadata, row);
                    }

                    @Override
                    public void remove() {
                        rowIterator.remove();
                    }
                };
            }
        };
    }

    @Override
    public  T findById(Class rowClass, String id, SimpleOrmContext context) {
        try {
            ModelMetadata modelMetadata = ModelMetadata.getModelMetadata(rowClass);
            final Scanner scanner = createScanner(getTableName(modelMetadata), (AccumuloSimpleOrmContext) context);
            try {
                scanner.setRange(new Range(id));
                Iterator rows = scannerToRows(scanner, modelMetadata).iterator();
                if (!rows.hasNext()) {
                    return null;
                }
                T result = rows.next();
                if (rows.hasNext()) {
                    throw new SimpleOrmException("Too many rows returned for a single row query (rowKey: " + id + ")");
                }
                return result;
            } finally {
                scanner.close();
            }
        } catch (TableNotFoundException e) {
            throw new SimpleOrmException("Could not find by id", e);
        }
    }

    @Override
    public  Iterable findByIdStartsWith(Class rowClass, String idPrefix, SimpleOrmContext context) {
        try {
            ModelMetadata modelMetadata = ModelMetadata.getModelMetadata(rowClass);
            final Scanner scanner = createScanner(getTableName(modelMetadata), (AccumuloSimpleOrmContext) context);
            scanner.setRange(Range.prefix(idPrefix));
            return scannerToRows(scanner, modelMetadata);
        } catch (TableNotFoundException e) {
            throw new SimpleOrmException("Could not find by id starts with", e);
        }
    }

    @Override
    public  void alterVisibility(T obj, String currentVisibility, String newVisibility, SimpleOrmContext context) {
        try {
            ModelMetadata modelMetadata = ModelMetadata.getModelMetadata(obj);
            final Scanner scanner = createScanner(getTableName(modelMetadata), (AccumuloSimpleOrmContext) context);
            scanner.setRange(new Range(modelMetadata.getId(obj)));
            RowIterator rowIterator = new RowIterator(scanner);
            BatchWriter writer = getBatchWriter(getTableName(modelMetadata));
            ColumnVisibility newColumnVisibility = new ColumnVisibility(newVisibility);
            while (rowIterator.hasNext()) {
                Iterator> row = rowIterator.next();
                alterVisibilityRow(writer, row, currentVisibility, newColumnVisibility);
            }
            writer.flush();
        } catch (Throwable ex) {
            throw new SimpleOrmException("Could not alterVisibility", ex);
        }
    }

    private void alterVisibilityRow(BatchWriter writer, Iterator> row, String currentVisibility, ColumnVisibility newVisibility) throws MutationsRejectedException {
        Mutation mAdd = null;
        Mutation mDelete = null;
        boolean hasItems = false;
        while (row.hasNext()) {
            Map.Entry column = row.next();
            if (mAdd == null) {
                mAdd = new Mutation(column.getKey().getRow());
                mDelete = new Mutation(column.getKey().getRow());
            }
            if (column.getKey().getColumnVisibility().toString().equals(currentVisibility)) {
                hasItems = true;
                mAdd.put(
                        column.getKey().getColumnFamily(),
                        column.getKey().getColumnQualifier(),
                        newVisibility,
                        column.getKey().getTimestamp(),
                        column.getValue()
                );
                mDelete.putDelete(
                        column.getKey().getColumnFamily(),
                        column.getKey().getColumnQualifier()
                );
            }
        }
        if (hasItems) {
            writer.addMutation(mAdd);
            writer.addMutation(mDelete);
        }
    }

    @Override
    public  void save(T obj, String visibility, SimpleOrmContext context) {
        try {
            ModelMetadata modelMetadata = ModelMetadata.getModelMetadata(obj);
            ModelMetadata.Type modelMetadataType = modelMetadata.getTypeFromObject(obj);
            ensureTableIsInitialized(getTableName(modelMetadata));
            BatchWriter writer = getBatchWriter(getTableName(modelMetadata));
            ColumnVisibility columnVisibility = new ColumnVisibility(visibility);
            writer.addMutation(getMutationForObject(modelMetadata, modelMetadataType, obj, columnVisibility));
            writer.flush();
        } catch (MutationsRejectedException e) {
            throw new SimpleOrmException("Error occurred when writing mutation", e);
        }
    }

    @Override
    public  void saveMany(Collection objs, String visibility, SimpleOrmContext context) {
        try {
            if (objs.size() == 0) {
                return;
            }
            BatchWriter writer = null;
            ModelMetadata modelMetadata = null;
            ColumnVisibility columnVisibility = new ColumnVisibility(visibility);
            for (T obj : objs) {
                if (modelMetadata == null) {
                    modelMetadata = ModelMetadata.getModelMetadata(obj);
                    ensureTableIsInitialized(getTableName(modelMetadata));
                }
                if (writer == null) {
                    writer = getBatchWriter(getTableName(modelMetadata));
                }
                writer.addMutation(getMutationForObject(modelMetadata, modelMetadata.getTypeFromObject(obj), obj, columnVisibility));
            }
            if (writer != null) {
                writer.flush();
            }
        } catch (MutationsRejectedException e) {
            throw new SimpleOrmException("Error occurred when writing mutation", e);
        }
    }


    public  String getTableName(Class rowClass) {
        ModelMetadata modelMetadata = ModelMetadata.getModelMetadata(rowClass);
        return getTableName(modelMetadata);
    }

    private  String getTableName(ModelMetadata modelMetadata) {
        return tablePrefix + modelMetadata.getTableName();
    }

    public  Mutation getMutationForObject(T obj, String visibility) {
        ModelMetadata modelMetadata = ModelMetadata.getModelMetadata(obj);
        ModelMetadata.Type type = modelMetadata.getTypeFromObject(obj);
        return getMutationForObject(modelMetadata, type, obj, new ColumnVisibility(visibility));
    }

    private  Mutation getMutationForObject(ModelMetadata modelMetadata, ModelMetadata.Type type, T obj, ColumnVisibility columnVisibility) {
        String rowKey = modelMetadata.getId(obj);
        Mutation mutation = new Mutation(rowKey);
        for (Map.Entry> columnFamilyFields : type.getFields().entrySet()) {
            Text columnFamilyName = new Text(columnFamilyFields.getKey());
            for (Map.Entry columnField : columnFamilyFields.getValue().entrySet()) {
                Text columnName = new Text(columnField.getKey());
                byte[] value = columnField.getValue().get(obj);
                if (value == null) {
                    continue;
                }
                mutation.put(columnFamilyName, columnName, columnVisibility, new Value(value));
            }
        }
        return mutation;
    }

    private BatchWriter getBatchWriter(String tableName) {
        try {
            synchronized (batchWriters) {
                BatchWriter writer = batchWriters.get(tableName);
                if (writer == null) {
                    writer = connector.createBatchWriter(tableName, batchWriterConfig);
                    batchWriters.put(tableName, writer);
                }
                return writer;
            }
        } catch (TableNotFoundException e) {
            throw new SimpleOrmException("Could not find table: " + tableName, e);
        }
    }

    private  T createObjectFromRow(ModelMetadata modelMetadata, Iterator> row) {
        try {
            boolean first = true;
            String discriminatorValue;
            Iterator columns;
            if (modelMetadata.getDiscriminatorColumnName() == null) {
                discriminatorValue = ModelMetadata.DEFAULT_DISCRIMINATOR;
                columns = ColumnData.createIterator(row);
            } else {
                RowData rowData = new RowData(row);
                byte[] discriminatorValueBytes = rowData.getColumnValue(modelMetadata.getDiscriminatorColumnFamily(), modelMetadata.getDiscriminatorColumnName());
                checkNotNull(discriminatorValueBytes, "Could not find discriminatorValue " + modelMetadata.getDiscriminatorColumnFamily() + "." + modelMetadata.getDiscriminatorColumnName());
                discriminatorValue = new String(discriminatorValueBytes);
                columns = rowData.createIterator();
            }
            ModelMetadata.Type type = modelMetadata.getType(discriminatorValue);
            T result = type.newInstance();
            while (columns.hasNext()) {
                ColumnData column = columns.next();
                if (first) {
                    modelMetadata.setIdField(result, column.getRowKey());
                }

                String columnFamily = column.getColumnFamily();
                String columnName = column.getColumnName();
                ModelMetadata.Field field = type.getFieldForColumn(columnFamily, columnName);
                if (field != null) {
                    field.set(result, column.getValue());
                }
                first = false;
            }
            return result;
        } catch (Exception e) {
            throw new SimpleOrmException("Could not create class: " + modelMetadata.toString(), e);
        }
    }

    private static class RowData {
        private String rowKey;
        private final Map> data = new HashMap<>();

        public RowData(Iterator> row) {
            while (row.hasNext()) {
                Map.Entry column = row.next();
                if (rowKey == null) {
                    rowKey = column.getKey().getRow().toString();
                }
                addColumn(column);
            }
        }

        private void addColumn(Map.Entry column) {
            Map columnFamilyData = getColumnFamilyData(column.getKey().getColumnFamily().toString());
            columnFamilyData.put(column.getKey().getColumnQualifier().toString(), column.getValue().get());
        }

        private Map getColumnFamilyData(String columnFamilyName) {
            Map columnFamilyData = data.get(columnFamilyName);
            if (columnFamilyData == null) {
                columnFamilyData = new HashMap<>();
                data.put(columnFamilyName, columnFamilyData);
            }
            return columnFamilyData;
        }

        public byte[] getColumnValue(String columnFamily, String columnName) {
            return getColumnFamilyData(columnFamily).get(columnName);
        }

        public Iterator createIterator() {
            List results = new ArrayList<>();
            for (Map.Entry> columnFamily : data.entrySet()) {
                String columnFamilyName = columnFamily.getKey();
                for (Map.Entry column : columnFamily.getValue().entrySet()) {
                    String columnName = column.getKey();
                    byte[] value = column.getValue();
                    results.add(new ColumnData(rowKey, columnFamilyName, columnName, value));
                }
            }
            return results.iterator();
        }
    }

    private static class ColumnData {
        private final String rowKey;
        private final String columnFamily;
        private final String columnName;
        private final byte[] value;

        private ColumnData(String rowKey, String columnFamily, String columnName, byte[] value) {
            this.rowKey = rowKey;
            this.columnFamily = columnFamily;
            this.columnName = columnName;
            this.value = value;
        }

        public String getRowKey() {
            return rowKey;
        }

        public String getColumnFamily() {
            return columnFamily;
        }

        public String getColumnName() {
            return columnName;
        }

        public byte[] getValue() {
            return value;
        }

        public static Iterator createIterator(final Iterator> row) {
            return new Iterator() {
                @Override
                public boolean hasNext() {
                    return row.hasNext();
                }

                @Override
                public ColumnData next() {
                    Map.Entry column = row.next();
                    String rowKey = column.getKey().getRow().toString();
                    String columnFamily = column.getKey().getColumnFamily().toString();
                    String columnName = column.getKey().getColumnQualifier().toString();
                    byte[] value = column.getValue().get();
                    return new ColumnData(rowKey, columnFamily, columnName, value);
                }

                @Override
                public void remove() {
                    throw new SimpleOrmException("Not supported");
                }
            };
        }
    }

    private Scanner createScanner(String tableName, AccumuloSimpleOrmContext context) throws TableNotFoundException {
        ensureTableIsInitialized(tableName);

        Scanner scanner = connector.createScanner(tableName, context.getAuthorizations());
        IteratorSetting iteratorSetting = new IteratorSetting(
                100,
                RowDeletingIterator.class.getSimpleName(),
                RowDeletingIterator.class
        );
        scanner.addScanIterator(iteratorSetting);
        return scanner;
    }

    private void ensureTableIsInitialized(String tableName) {
        try {
            if (initializedTables.contains(tableName)) {
                return;
            }

            if (!connector.tableOperations().list().contains(tableName)) {
                LOGGER.info("creating table: " + tableName);
                try {
                    connector.tableOperations().create(tableName);
                } catch (TableExistsException e) {
                    // This sometimes happens. Just ignore since the table is present.
                }
            }

            IteratorSetting is = new IteratorSetting(ROW_DELETING_ITERATOR_PRIORITY, ROW_DELETING_ITERATOR_NAME, RowDeletingIterator.class);
            if (!connector.tableOperations().listIterators(tableName).containsKey(ROW_DELETING_ITERATOR_NAME)) {
                try {
                    connector.tableOperations().attachIterator(tableName, is);
                } catch (AccumuloException e) {
                    absorbIteratorNameConflictException(e);
                }
            }
            initializedTables.add(tableName);
        } catch (Exception e) {
            throw new SimpleOrmException("Could not initialize table", e);
        }
    }

    private void absorbIteratorNameConflictException(AccumuloException ex) throws AccumuloException {
        // Like the TableExistsException, this sometimes happens.
        // The exception inspection here depends on the implementation of
        // org.apache.accumulo.core.client.impl.TableOperationsHelper#checkIteratorConflicts(), and is therefore
        // fragile.
        boolean isErrorOk = false;
        Throwable cause = ex.getCause();
        if (cause instanceof IllegalArgumentException) {
            if (cause.getMessage().contains("iterator name conflict")) {
                isErrorOk = true;
            }
        }
        if (!isErrorOk) {
            throw ex;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy