org.zenoss.zep.dao.impl.EventIndexQueueDaoImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.zenoss.zep.dao.impl.EventIndexQueueDaoImpl.java

Source

/*****************************************************************************
 * 
 * Copyright (C) Zenoss, Inc. 2011, all rights reserved.
 * 
 * This content is made available according to terms specified in
 * License.zenoss under the directory where your Zenoss product is installed.
 * 
 ****************************************************************************/

package org.zenoss.zep.dao.impl;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcOperations;
import org.springframework.transaction.annotation.Transactional;
import org.zenoss.protobufs.zep.Zep.EventSummary;
import org.zenoss.zep.ZepException;
import org.zenoss.zep.annotations.TransactionalRollbackAllExceptions;
import org.zenoss.zep.dao.EventIndexHandler;
import org.zenoss.zep.dao.EventIndexQueueDao;
import org.zenoss.zep.dao.impl.compat.DatabaseCompatibility;
import org.zenoss.zep.dao.impl.compat.TypeConverter;
import org.zenoss.zep.events.EventIndexQueueSizeEvent;
import org.zenoss.zep.dao.impl.SimpleJdbcTemplateProxy;

import java.lang.reflect.Proxy;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Gauge;

import javax.annotation.Resource;

/**
 * Implementation of EventIndexQueueDao.
 */
public class EventIndexQueueDaoImpl implements EventIndexQueueDao, ApplicationEventPublisherAware {

    private final SimpleJdbcOperations template;
    private final String queueTableName;
    private final String tableName;
    private final EventSummaryRowMapper rowMapper;
    private ApplicationEventPublisher applicationEventPublisher;

    private final TypeConverter<String> uuidConverter;
    private final TypeConverter<Long> timestampConverter;

    private final boolean isArchive;

    private MetricRegistry metrics;
    private int lastQueueSize = -1;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public EventIndexQueueDaoImpl(DataSource ds, boolean isArchive, EventDaoHelper daoHelper,
            DatabaseCompatibility databaseCompatibility) {
        this.template = (SimpleJdbcOperations) Proxy.newProxyInstance(SimpleJdbcOperations.class.getClassLoader(),
                new Class<?>[] { SimpleJdbcOperations.class }, new SimpleJdbcTemplateProxy(ds));
        this.isArchive = isArchive;
        if (isArchive) {
            this.tableName = EventConstants.TABLE_EVENT_ARCHIVE;
        } else {
            this.tableName = EventConstants.TABLE_EVENT_SUMMARY;
        }
        this.queueTableName = this.tableName + "_index_queue";
        this.uuidConverter = databaseCompatibility.getUUIDConverter();
        this.timestampConverter = databaseCompatibility.getTimestampConverter();
        this.rowMapper = new EventSummaryRowMapper(daoHelper, databaseCompatibility);
    }

    @Override
    @TransactionalRollbackAllExceptions
    public List<Long> indexEvents(final EventIndexHandler handler, final int limit) throws ZepException {
        return indexEvents(handler, limit, -1L);
    }

    @Resource(name = "metrics")
    public void setBean(MetricRegistry metrics) {
        this.metrics = metrics;
        String metricName = "";
        if (this.isArchive) {
            metricName = MetricRegistry.name(this.getClass().getCanonicalName(), "archiveIndexQueueSize");
        } else {
            metricName = MetricRegistry.name(this.getClass().getCanonicalName(), "summaryIndexQueueSize");
        }
        this.metrics.register(metricName, new Gauge<Integer>() {
            @Override
            public Integer getValue() {
                return lastQueueSize;
            }
        });
    }

    @Override
    @TransactionalRollbackAllExceptions
    public List<Long> indexEvents(final EventIndexHandler handler, final int limit, final long maxUpdateTime)
            throws ZepException {
        final Map<String, Object> selectFields = new HashMap<String, Object>();
        selectFields.put("_limit", limit);

        final String sql;

        // Used for partition pruning
        final String queryJoinLastSeen = (this.isArchive) ? "AND iq.last_seen=es.last_seen " : "";

        if (maxUpdateTime > 0L) {
            selectFields.put("_max_update_time", timestampConverter.toDatabaseType(maxUpdateTime));
            sql = "SELECT iq.id AS iq_id, iq.uuid AS iq_uuid, iq.update_time AS iq_update_time," + "es.* FROM "
                    + this.queueTableName + " AS iq " + "LEFT JOIN " + this.tableName + " es ON iq.uuid=es.uuid "
                    + queryJoinLastSeen + "WHERE iq.update_time <= :_max_update_time "
                    + "ORDER BY iq_id LIMIT :_limit";
        } else {
            sql = "SELECT iq.id AS iq_id, iq.uuid AS iq_uuid, iq.update_time AS iq_update_time," + "es.* FROM "
                    + this.queueTableName + " AS iq " + "LEFT JOIN " + this.tableName + " es ON iq.uuid=es.uuid "
                    + queryJoinLastSeen + "ORDER BY iq_id LIMIT :_limit";
        }

        final Set<String> eventUuids = new HashSet<String>();
        final List<Long> indexQueueIds = this.template.query(sql, new RowMapper<Long>() {
            @Override
            public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
                final long iqId = rs.getLong("iq_id");
                final String iqUuid = uuidConverter.fromDatabaseType(rs, "iq_uuid");
                // Don't process the same event multiple times.
                if (eventUuids.add(iqUuid)) {
                    final Object uuid = rs.getObject("uuid");
                    if (uuid != null) {
                        EventSummary summary = rowMapper.mapRow(rs, rowNum);
                        try {
                            handler.handle(summary);
                        } catch (Exception e) {
                            throw new RuntimeException(e.getLocalizedMessage(), e);
                        }
                    } else {
                        try {
                            handler.handleDeleted(iqUuid);
                        } catch (Exception e) {
                            throw new RuntimeException(e.getLocalizedMessage(), e);
                        }
                    }
                }
                return iqId;
            }
        }, selectFields);

        if (!indexQueueIds.isEmpty()) {
            try {
                handler.handleComplete();
            } catch (Exception e) {
                throw new ZepException(e.getLocalizedMessage(), e);
            }
        }

        // publish current size of event_*_index_queue table
        this.lastQueueSize = this.template.queryForInt("SELECT COUNT(1) FROM " + this.queueTableName);
        this.applicationEventPublisher
                .publishEvent(new EventIndexQueueSizeEvent(this, tableName, this.lastQueueSize, limit));

        return indexQueueIds;
    }

    @Override
    @Transactional(readOnly = true)
    public long getQueueLength() {
        String sql = String.format("SELECT COUNT(*) FROM %s", queueTableName);
        return template.queryForLong(sql);
    }

    @Override
    @TransactionalRollbackAllExceptions
    public int deleteIndexQueueIds(List<Long> queueIds) throws ZepException {
        if (queueIds.isEmpty()) {
            return 0;
        }
        final String deleteSql = "DELETE FROM " + this.queueTableName + " WHERE id IN (:_iq_ids)";
        final Map<String, List<Long>> deleteFields = Collections.singletonMap("_iq_ids", queueIds);
        return this.template.update(deleteSql, deleteFields);
    }
}