com.melexis.esb.eventstore.impl.EventDaoCassandraImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.melexis.esb.eventstore.impl.EventDaoCassandraImpl.java

Source

/*
 * Copyright 2011 Melexis NV
 *
 *    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 com.melexis.esb.eventstore.impl;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.melexis.esb.eventstore.Event;
import me.prettyprint.cassandra.model.IndexedSlicesQuery;
import me.prettyprint.cassandra.serializers.StringSerializer;
import me.prettyprint.cassandra.service.ThriftColumnDef;
import me.prettyprint.cassandra.service.ThriftKsDef;
import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate;
import me.prettyprint.cassandra.service.template.ColumnFamilyUpdater;
import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate;
import me.prettyprint.hector.api.Cluster;
import me.prettyprint.hector.api.Keyspace;
import me.prettyprint.hector.api.beans.HColumn;
import me.prettyprint.hector.api.beans.Row;
import me.prettyprint.hector.api.ddl.*;
import me.prettyprint.hector.api.factory.HFactory;
import org.apache.cassandra.thrift.ColumnDef;
import org.apache.cassandra.thrift.IndexType;
import org.joda.time.DateTime;

import javax.annotation.Nullable;
import java.util.*;

import static me.prettyprint.hector.api.factory.HFactory.createIndexedSlicesQuery;

public class EventDaoCassandraImpl implements EventDao {

    public static final StringSerializer SERIALIZER = StringSerializer.get();
    public static final String LOTNAME = "LOTNAME";
    public static final String SOURCE = "SOURCE";
    public static final String PROCESSID = "PROCESSID";
    public static final String TIMESTAMP = "TIMESTAMP";
    public static final Function<Row<String, String, String>, Event> ROW_TO_EVENT_FN = new Function<Row<String, String, String>, Event>() {
        @Override
        public Event apply(@Nullable Row<String, String, String> row) {
            Map<String, String> attributes = new HashMap<String, String>();
            final List<HColumn<String, String>> columns = row.getColumnSlice().getColumns();
            String source = "";
            DateTime ts = null;
            for (HColumn<String, String> column : columns) {
                if (column.getName().equals(SOURCE)) {
                    source = column.getValue();
                } else if (column.getName().equals(TIMESTAMP)) {
                    ts = new DateTime(column.getValue());
                } else {
                    String key = column.getName();
                    String value = column.getValue();
                    attributes.put(key, value);
                }
            }

            final Event event = Event.createEvent(ts, source, attributes);
            return event;
        }
    };

    private final Keyspace keyspace;
    private final String columnFamily;
    private final int replicationFactor;

    public Keyspace getKeyspace() {
        return keyspace;
    }

    public String getColumnFamily() {
        return columnFamily;
    }

    public EventDaoCassandraImpl(Cluster cluster, String keyspaceName, String columnFamily) {
        this(cluster, keyspaceName, columnFamily, 3);
    }

    public EventDaoCassandraImpl(Cluster cluster, String keyspaceName, String columnFamily, int replicationFactor) {
        this.replicationFactor = replicationFactor;
        this.columnFamily = columnFamily;

        KeyspaceDefinition keyspaceDef = cluster.describeKeyspace(keyspaceName);

        // If keyspace does not exist, the CFs don't exist either. => create them.
        // TODO: This is a blunt instrument. Needs refactoring when adding a new DAO.
        if (keyspaceDef == null) {
            createSchema(cluster, keyspaceName, columnFamily);
        }
        this.keyspace = HFactory.createKeyspace(keyspaceName, cluster);
    }

    private void createSchema(Cluster cluster, String keyspace, String columnFamily) {

        List<ColumnDef> columns = new ArrayList<ColumnDef>();
        columns.add(newIndexedColumnDef(LOTNAME, "UTF8Type"));
        columns.add(newIndexedColumnDef(SOURCE, "UTF8Type"));
        columns.add(newIndexedColumnDef(PROCESSID, "UTF8Type"));
        columns.add(newIndexedColumnDef(TIMESTAMP, "UTF8Type"));
        List<ColumnDefinition> columnMetadata = ThriftColumnDef.fromThriftList(columns);

        ColumnFamilyDefinition cfDef = HFactory.createColumnFamilyDefinition(keyspace, columnFamily,
                ComparatorType.UTF8TYPE, columnMetadata);
        cfDef.setColumnType(ColumnType.STANDARD);
        cfDef.setKeyValidationClass("org.apache.cassandra.db.marshal.UTF8Type");
        cfDef.setDefaultValidationClass("org.apache.cassandra.db.marshal.UTF8Type");

        KeyspaceDefinition newKeyspace = HFactory.createKeyspaceDefinition(keyspace, ThriftKsDef.DEF_STRATEGY_CLASS,
                replicationFactor, Arrays.asList(cfDef));

        // Add the schema to the cluster.
        cluster.addKeyspace(newKeyspace);
    }

    public void store(Event event) {

        ColumnFamilyTemplate<String, String> template = new ThriftColumnFamilyTemplate<String, String>(keyspace,
                columnFamily, SERIALIZER, SERIALIZER);

        ColumnFamilyUpdater<String, String> updater = template.createUpdater(UUID.randomUUID().toString());
        updater.setString(SOURCE, event.getSource());
        updater.setString(TIMESTAMP, event.getTimestamp().toString());
        for (Map.Entry<String, String> entry : event.getAttributes().entrySet()) {
            updater.setString(entry.getKey(), entry.getValue());
        }
        template.update(updater);

    }

    public List<Event> findEvents(String source, DateTime start, DateTime end, int max) {

        String from = (start == null) ? "" : start.toString();
        String till = (end == null) ? "" : end.toString();

        IndexedSlicesQuery<String, String, String> query = createIndexedSlicesQuery(keyspace, SERIALIZER,
                SERIALIZER, SERIALIZER);
        query.setColumnFamily(columnFamily);

        query.addEqualsExpression(SOURCE, source);

        addDateTimeConstraints(start, end, from, till, query);

        query.setRange("A", "z", false, 1000);

        List<Row<String, String, String>> rows = query.execute().get().getList();

        final List<Event> results = Lists.transform(rows, ROW_TO_EVENT_FN);

        return orderedResultSet(from, till, results, max);
    }

    @Override
    public List<Event> findEventsForLotnameAndSource(final String lotname, final String source,
            @Nullable DateTime start, @Nullable DateTime end, int max) {
        String from = (start == null) ? "" : start.toString();
        String till = (end == null) ? "" : end.toString();

        IndexedSlicesQuery<String, String, String> query = createIndexedSlicesQuery(keyspace, SERIALIZER,
                SERIALIZER, SERIALIZER);

        query.setColumnFamily(columnFamily);

        query.addEqualsExpression(LOTNAME, lotname);
        query.addEqualsExpression(SOURCE, source);
        query.setRange("A", "z", false, 1000);
        query.setStartKey("");

        addDateTimeConstraints(start, end, from, till, query);

        final List<Row<String, String, String>> rows = query.execute().get().getList();
        final List<Event> results = Lists.transform(rows, ROW_TO_EVENT_FN);

        return orderedResultSet(from, till, results, max);
    }

    @Override
    public List<Event> findEventsForProcessIdAndSource(final String processId, final String source,
            @Nullable DateTime start, @Nullable DateTime end, int max) {
        String from = (start == null) ? "" : start.toString();
        String till = (end == null) ? "" : end.toString();

        IndexedSlicesQuery<String, String, String> query = createIndexedSlicesQuery(keyspace, SERIALIZER,
                SERIALIZER, SERIALIZER);
        query.setColumnFamily(columnFamily);

        query.addEqualsExpression(PROCESSID, processId);
        query.addEqualsExpression(SOURCE, source);
        query.setRange("A", "z", false, 1000);
        query.setStartKey("");

        addDateTimeConstraints(start, end, from, till, query);

        final List<Row<String, String, String>> rows = query.execute().get().getList();
        final List<Event> results = Lists.transform(rows, ROW_TO_EVENT_FN);

        return orderedResultSet(from, till, results, max);
    }

    private final static void addDateTimeConstraints(DateTime start, DateTime end, String from, String till,
            IndexedSlicesQuery<String, String, String> query) {
        if ((till.compareTo(from) > 0) || till.equals("")) {
            query.addGteExpression(TIMESTAMP, from);
            if (end != null) {
                query.addLteExpression(TIMESTAMP, till);
            }
        } else {
            // return rows in reverse order
            if (start != null) {
                query.addGteExpression(TIMESTAMP, till);
            }
            query.addLteExpression(TIMESTAMP, from);
        }
    }

    private final static ColumnDef newIndexedColumnDef(String column_name, String comparer) {
        final StringSerializer ss = StringSerializer.get();
        final ColumnDef cd = new ColumnDef(ss.toByteBuffer(column_name), comparer);
        cd.setIndex_name(column_name);
        cd.setIndex_type(IndexType.KEYS);
        return cd;
    }

    private final static List<Event> orderedResultSet(String from, String till, List<Event> results, int max) {
        final int toIndex = max > results.size() ? results.size() : max;
        List<Event> events;
        if ((till.compareTo(from) > 0) || till.equals("")) {
            events = Ordering.natural().onResultOf(new Function<Event, Comparable>() {
                @Override
                public Comparable apply(@Nullable Event event) {
                    return event.getTimestamp();
                }
            }).sortedCopy(results);
        } else {
            events = Ordering.natural().onResultOf(new Function<Event, Comparable>() {
                @Override
                public Comparable apply(@Nullable Event event) {
                    return event.getTimestamp();
                }
            }).reverse().sortedCopy(results);
        }
        return events.subList(0, toIndex);
    }
}