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

Java tutorial

Introduction

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

Source

/*****************************************************************************
 * 
 * Copyright (C) Zenoss, Inc. 2010-2011, 2014 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 com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Message;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.simple.SimpleJdbcOperations;
import org.zenoss.protobufs.JsonFormat;
import org.zenoss.protobufs.zep.Zep.ZepConfig;
import org.zenoss.protobufs.zep.Zep.ZepConfig.Builder;
import org.zenoss.zep.Messages;
import org.zenoss.zep.ZepException;
import org.zenoss.zep.annotations.TransactionalReadOnly;
import org.zenoss.zep.annotations.TransactionalRollbackAllExceptions;
import org.zenoss.zep.dao.ConfigDao;
import org.zenoss.zep.dao.impl.compat.NestedTransactionService;
import org.zenoss.zep.dao.impl.SimpleJdbcTemplateProxy;

import java.lang.reflect.Proxy;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static org.zenoss.zep.dao.impl.EventConstants.*;

public class ConfigDaoImpl implements ConfigDao {

    private static final Logger logger = LoggerFactory.getLogger(ConfigDao.class);
    private final SimpleJdbcOperations template;
    private Messages messages;
    private static final int MAX_PARTITIONS = 1000;
    private final int maxEventArchivePurgeIntervalDays;
    private int maxEventArchiveIntervalMinutes = 30 * 24 * 60;
    private static final long MIN_EVENT_SIZE_MAX_BYTES = 8 * 1024;
    private static final long MAX_EVENT_SIZE_MAX_BYTES = 100 * 1024;
    private NestedTransactionService nestedTransactionService;

    private static final String COLUMN_CONFIG_NAME = "config_name";
    private static final String COLUMN_CONFIG_VALUE = "config_value";

    public ConfigDaoImpl(DataSource ds, PartitionConfig partitionConfig) {
        this.template = (SimpleJdbcOperations) Proxy.newProxyInstance(SimpleJdbcOperations.class.getClassLoader(),
                new Class<?>[] { SimpleJdbcOperations.class }, new SimpleJdbcTemplateProxy(ds));

        this.maxEventArchivePurgeIntervalDays = calculateMaximumDays(
                partitionConfig.getConfig(TABLE_EVENT_ARCHIVE));
        logger.info("Maximum archive days: {}", maxEventArchivePurgeIntervalDays);
    }

    public void setNestedTransactionService(NestedTransactionService nestedTransactionService) {
        this.nestedTransactionService = nestedTransactionService;
    }

    private static int calculateMaximumDays(PartitionTableConfig config) {
        long partitionRange = config.getPartitionUnit().toMinutes(config.getPartitionDuration()) * MAX_PARTITIONS;
        return (int) TimeUnit.DAYS.convert(partitionRange, TimeUnit.MINUTES);
    }

    @Autowired
    public void setMessages(Messages messages) {
        this.messages = messages;
    }

    public void setMaxEventArchiveIntervalMinutes(int maxEventArchiveIntervalDays) {
        this.maxEventArchiveIntervalMinutes = maxEventArchiveIntervalDays;
    }

    @Override
    @TransactionalReadOnly
    public ZepConfig getConfig() throws ZepException {
        final String sql = "SELECT * FROM config";
        ZepConfig config = template.getJdbcOperations().query(sql, new ZepConfigExtractor());
        validateConfig(config);
        return config;
    }

    @Override
    @TransactionalRollbackAllExceptions
    public void setConfig(ZepConfig config) throws ZepException {
        validateConfig(config);
        final List<Map<String, String>> records = configToRecords(config);
        for (Map<String, String> record : records) {
            setConfigValueInternal(record);
        }
    }

    private void setConfigValueInternal(final Map<String, String> fields) throws ZepException {
        final String insertSql = "INSERT INTO config (config_name,config_value) VALUES(:config_name,:config_value)";
        final String updateSql = "UPDATE config SET config_value=:config_value WHERE config_name=:config_name";
        DaoUtils.insertOrUpdate(nestedTransactionService, template, insertSql, updateSql, fields);
    }

    @Override
    @TransactionalRollbackAllExceptions
    public int removeConfigValue(String name) throws ZepException {
        final String sql = "DELETE FROM config WHERE config_name=:config_name";
        return this.template.update(sql, Collections.singletonMap(COLUMN_CONFIG_NAME, name));
    }

    @Override
    @TransactionalRollbackAllExceptions
    public void setConfigValue(String name, ZepConfig config) throws ZepException {
        validateConfig(config);
        FieldDescriptor field = ZepConfig.getDescriptor().findFieldByName(name);
        if (field == null) {
            throw new ZepException("Invalid field name: " + name);
        }
        Object value = config.getField(field);
        if (value == null) {
            removeConfigValue(name);
        }
        final Map<String, String> fields = new HashMap<String, String>(2);
        fields.put(COLUMN_CONFIG_NAME, name);
        fields.put(COLUMN_CONFIG_VALUE, valueToString(field, value));
        setConfigValueInternal(fields);
    }

    private void validateConfig(ZepConfig config) throws ZepException {
        int ageIntervalMinutes = config.getEventAgeIntervalMinutes();
        if (ageIntervalMinutes < 0) {
            throw new ZepException(messages.getMessage("invalid_event_age_interval"));
        }

        long agingIntervalMilliseconds = config.getAgingIntervalMilliseconds();
        if (agingIntervalMilliseconds < 1L) {
            throw new ZepException(messages.getMessage("invalid_aging_interval_milliseconds"));
        }

        int agingLimit = config.getAgingLimit();
        if (agingLimit < 1) {
            throw new ZepException(messages.getMessage("invalid_aging_limit"));
        }

        int eventArchivePurgeIntervalDays = config.getEventArchivePurgeIntervalDays();
        if (eventArchivePurgeIntervalDays < 1 || eventArchivePurgeIntervalDays > maxEventArchivePurgeIntervalDays) {
            throw new ZepException(messages.getMessage("invalid_event_archive_purge_interval", 1,
                    maxEventArchivePurgeIntervalDays));
        }

        int eventArchiveIntervalMinutes = config.getEventArchiveIntervalMinutes();
        if (eventArchiveIntervalMinutes < 1 || eventArchiveIntervalMinutes > maxEventArchiveIntervalMinutes) {
            throw new ZepException(
                    messages.getMessage("invalid_event_archive_interval", 1, maxEventArchiveIntervalMinutes));
        }

        long archiveIntervalMilliseconds = config.getArchiveIntervalMilliseconds();
        if (archiveIntervalMilliseconds < 1L) {
            throw new ZepException(messages.getMessage("invalid_archive_interval_milliseconds"));
        }

        int archiveLimit = config.getArchiveLimit();
        if (archiveLimit < 1) {
            throw new ZepException(messages.getMessage("invalid_archive_limit"));
        }

        long eventMaxSizeBytes = config.getEventMaxSizeBytes();
        if (eventMaxSizeBytes < MIN_EVENT_SIZE_MAX_BYTES || eventMaxSizeBytes > MAX_EVENT_SIZE_MAX_BYTES) {
            throw new ZepException(messages.getMessage("invalid_event_max_size_bytes", MIN_EVENT_SIZE_MAX_BYTES,
                    MAX_EVENT_SIZE_MAX_BYTES));
        }

        long indexSummaryIntervalMilliseconds = config.getIndexSummaryIntervalMilliseconds();
        if (indexSummaryIntervalMilliseconds < 1L) {
            throw new ZepException(messages.getMessage("invalid_index_summary_interval_milliseconds"));
        }

        long indexArchiveIntervalMilliseconds = config.getIndexArchiveIntervalMilliseconds();
        if (indexArchiveIntervalMilliseconds < 1L) {
            throw new ZepException(messages.getMessage("invalid_index_archive_interval_milliseconds"));
        }

        int indexLimit = config.getIndexLimit();
        if (indexLimit < 1) {
            throw new ZepException(messages.getMessage("invalid_index_limit"));
        }

        int eventTimePurgeIntervalDays = config.getEventTimePurgeIntervalDays();
        if (eventTimePurgeIntervalDays < 1) {
            throw new ZepException(messages.getMessage("invalid_event_time_purge_interval_days"));
        }
    }

    private static List<Map<String, String>> configToRecords(ZepConfig config) throws ZepException {
        final List<Map<String, String>> records = new ArrayList<Map<String, String>>();
        final Descriptor descriptor = config.getDescriptorForType();
        for (FieldDescriptor field : descriptor.getFields()) {
            final Object value = config.getField(field);
            if (value != null) {
                Map<String, String> record = new HashMap<String, String>(2);
                record.put(COLUMN_CONFIG_NAME, field.getName());
                record.put(COLUMN_CONFIG_VALUE, valueToString(field, value));
                records.add(record);
            }
        }
        return records;
    }

    private static String valueToString(FieldDescriptor field, Object value) throws ZepException {
        if (field.isRepeated()) {
            throw new ZepException("Repeated field not supported");
        }
        switch (field.getJavaType()) {
        case BOOLEAN:
            return Boolean.toString((Boolean) value);
        case BYTE_STRING:
            return new String(Base64.encodeBase64(((ByteString) value).toByteArray()), Charset.forName("US-ASCII"));
        case DOUBLE:
            return Double.toString((Double) value);
        case ENUM:
            return Integer.toString(((EnumValueDescriptor) value).getNumber());
        case FLOAT:
            return Float.toString((Float) value);
        case INT:
            return Integer.toString((Integer) value);
        case LONG:
            return Long.toString((Long) value);
        case MESSAGE:
            try {
                return JsonFormat.writeAsString((Message) value);
            } catch (IOException e) {
                throw new ZepException(e.getLocalizedMessage(), e);
            }
        case STRING:
            return (String) value;
        default:
            throw new ZepException("Unsupported type: " + field.getType());
        }
    }

    private static Object valueFromString(FieldDescriptor field, Builder builder, String strValue)
            throws ZepException {
        if (field.isRepeated()) {
            throw new ZepException("Repeated field not supported");
        }
        switch (field.getJavaType()) {
        case BOOLEAN:
            return Boolean.valueOf(strValue);
        case BYTE_STRING:
            try {
                return ByteString.copyFrom(Base64.decodeBase64(strValue.getBytes("US-ASCII")));
            } catch (UnsupportedEncodingException e) {
                // This exception should never happen - US-ASCII always exists in JVM
                throw new RuntimeException(e.getLocalizedMessage(), e);
            }
        case DOUBLE:
            return Double.valueOf(strValue);
        case ENUM:
            return field.getEnumType().findValueByNumber(Integer.valueOf(strValue));
        case FLOAT:
            return Float.valueOf(strValue);
        case INT:
            return Integer.valueOf(strValue);
        case LONG:
            return Long.valueOf(strValue);
        case MESSAGE:
            try {
                return JsonFormat.merge(strValue, builder.newBuilderForField(field));
            } catch (IOException e) {
                throw new ZepException(e.getLocalizedMessage(), e);
            }
        case STRING:
            return strValue;
        default:
            throw new ZepException("Unsupported type: " + field.getType());
        }
    }

    private static final class ZepConfigExtractor implements ResultSetExtractor<ZepConfig> {
        @Override
        public ZepConfig extractData(ResultSet rs) throws SQLException, DataAccessException {
            Descriptor descriptor = ZepConfig.getDescriptor();
            Builder configBuilder = ZepConfig.newBuilder();
            while (rs.next()) {
                final String fieldName = rs.getString(COLUMN_CONFIG_NAME);
                final String fieldValueStr = rs.getString(COLUMN_CONFIG_VALUE);
                FieldDescriptor field = descriptor.findFieldByName(fieldName);
                if (field == null) {
                    logger.warn("Unrecognized field: {}", fieldName);
                } else {
                    try {
                        Object fieldValue = valueFromString(field, configBuilder, fieldValueStr);
                        configBuilder.setField(field, fieldValue);
                    } catch (ZepException e) {
                        throw new SQLException(e.getLocalizedMessage(), e);
                    }
                }
            }
            return configBuilder.build();
        }
    }
}