monasca.api.infrastructure.persistence.hibernate.AlarmSqlRepoImpl.java Source code

Java tutorial

Introduction

Here is the source code for monasca.api.infrastructure.persistence.hibernate.AlarmSqlRepoImpl.java

Source

/*
 * Copyright 2015 FUJITSU LIMITED
 * Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
 *
 * 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 monasca.api.infrastructure.persistence.hibernate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import monasca.api.domain.exception.EntityNotFoundException;
import monasca.api.domain.model.alarm.Alarm;
import monasca.api.domain.model.alarm.AlarmCount;
import monasca.api.domain.model.alarm.AlarmRepo;
import monasca.api.resource.exception.Exceptions;
import monasca.common.hibernate.db.AlarmDb;
import monasca.common.hibernate.db.SubAlarmDb;
import monasca.common.hibernate.type.BinaryId;
import monasca.common.model.alarm.AlarmSeverity;
import monasca.common.model.alarm.AlarmState;
import monasca.common.model.alarm.AlarmSubExpression;
import monasca.common.model.metric.MetricDefinition;
import monasca.common.util.Conversions;

/**
 * Alarmed metric repository implementation.
 */
public class AlarmSqlRepoImpl extends BaseSqlRepo implements AlarmRepo {

    private static final Logger logger = LoggerFactory.getLogger(AlarmSqlRepoImpl.class);

    private static final String FIND_ALARM_BY_ID_SQL = "select distinct ad.id as alarm_definition_id, ad.severity, ad.name as alarm_definition_name, "
            + "a.id, a.state, a.updatedAt, a.createdAt as created_timestamp, "
            + "md.name as metric_name, mdg.id.name, mdg.value, a.lifecycleState, a.link, a.stateUpdatedAt, "
            + "mdg.id.dimensionSetId from AlarmDb as a " + ", AlarmDefinitionDb as ad " + ", AlarmMetricDb as am "
            + ", MetricDefinitionDimensionsDb as mdd " + ", MetricDefinitionDb as md "
            + ", MetricDimensionDb as mdg " + "where " + " ad.id = a.alarmDefinition.id "
            + " and am.alarmMetricId.alarm.id = a.id "
            + " and mdd.id = am.alarmMetricId.metricDefinitionDimensions.id "
            + " and md.id = mdd.metricDefinition.id " + " and mdg.id.dimensionSetId = mdd.metricDimensionSetId "
            + " and ad.tenantId = :tenantId " + " %s "
            + " and ad.deletedAt is null order by a.id, mdg.id.dimensionSetId %s";

    private static final String FIND_ALARMS_SQL = "select ad.id as alarm_definition_id, ad.severity, ad.name as alarm_definition_name, "
            + "a.id, a.state, a.updated_at as updated_timestamp, a.created_at as created_timestamp, "
            + "md.name as metric_name, mdg.name, mdg.value, a.lifecycle_state, a.link, a.state_updated_at as state_updated_timestamp, "
            + "mdg.dimension_set_id " + "from alarm as a "
            + "inner join %s as alarm_id_list on alarm_id_list.id = a.id "
            + "inner join alarm_definition ad on ad.id = a.alarm_definition_id "
            + "inner join alarm_metric as am on am.alarm_id = a.id "
            + "inner join metric_definition_dimensions as mdd on mdd.id = am.metric_definition_dimensions_id "
            + "inner join metric_definition as md on md.id = mdd.metric_definition_id "
            + "left outer join (select dimension_set_id, name, value "
            + "from metric_dimension group by dimension_set_id, name, value) as mdg on mdg.dimension_set_id = mdd.metric_dimension_set_id "
            + "order by a.id ASC";

    @Inject
    public AlarmSqlRepoImpl(@Named("orm") SessionFactory sessionFactory) {
        super(sessionFactory);
    }

    @Override
    public void deleteById(String tenantId, String id) {
        logger.trace(ORM_LOG_MARKER, "deleteById(...) entering");

        Transaction tx = null;
        Session session = null;
        try {
            session = sessionFactory.openSession();
            tx = session.beginTransaction();

            final long result = (Long) session.createCriteria(AlarmDb.class, "a")
                    .createAlias("alarmDefinition", "ad")
                    .add(Restrictions.conjunction(Restrictions.eq("a.id", id),
                            Restrictions.eq("ad.tenantId", tenantId),
                            Restrictions.eqProperty("a.alarmDefinition.id", "ad.id"),
                            Restrictions.isNull("ad.deletedAt")))
                    .setProjection(Projections.count("a.id")).setReadOnly(true).uniqueResult();

            // This will throw an EntityNotFoundException if Alarm doesn't exist or has a different tenant
            // id
            if (result < 1) {
                throw new EntityNotFoundException("No alarm exists for %s", id);
            }

            // delete alarm
            session.getNamedQuery(AlarmDb.Queries.DELETE_BY_ID).setString("id", id).executeUpdate();

            tx.commit();
            tx = null;
        } catch (Exception e) {
            this.rollbackIfNotNull(tx);
            throw e;
        } finally {
            if (session != null) {
                session.close();
            }
        }

    }

    @Override
    public List<Alarm> find(String tenantId, String alarmDefId, String metricName,
            Map<String, String> metricDimensions, AlarmState state, AlarmSeverity severity, String lifecycleState,
            String link, DateTime stateUpdatedStart, List<String> sortBy, String offset, int limit,
            boolean enforceLimit) {
        logger.trace(ORM_LOG_MARKER, "find(...) entering");
        if (sortBy != null && !sortBy.isEmpty()) {
            throw Exceptions.unprocessableEntity("Sort_by is not implemented for the hibernate database type");
        }
        if (severity != null) {
            throw Exceptions
                    .unprocessableEntity("Severity filter is not implemented for the hibernate database type");
        }

        List<Alarm> alarms = this.findInternal(tenantId, alarmDefId, metricName, metricDimensions, state,
                lifecycleState, link, stateUpdatedStart, offset, (3 * limit / 2), enforceLimit);

        if (limit == 0 || !enforceLimit)
            return alarms;

        if (alarms.size() > limit) {
            for (int i = alarms.size() - 1; i > limit; i--) {
                alarms.remove(i);
            }
        } else if (alarms.size() > 0) {
            while (alarms.size() < limit) {
                List<Alarm> alarms2;
                int diff = limit - alarms.size();
                String offset2 = alarms.get(alarms.size() - 1).getId();
                alarms2 = this.findInternal(tenantId, alarmDefId, metricName, metricDimensions, state,
                        lifecycleState, link, stateUpdatedStart, offset2, (2 * diff), enforceLimit);
                if (alarms2.size() == 0)
                    break;
                for (int i = 0; i < alarms2.size() && i < diff; i++)
                    alarms.add(alarms2.get(i));
            }
        }

        return alarms;
    }

    private List<Alarm> findInternal(String tenantId, String alarmDefId, String metricName,
            Map<String, String> metricDimensions, AlarmState state, String lifecycleState, String link,
            DateTime stateUpdatedStart, String offset, int limit, boolean enforceLimit) {
        Session session = null;

        List<Alarm> alarms = new LinkedList<>();

        try {
            Query query;
            session = sessionFactory.openSession();

            StringBuilder sbWhere = new StringBuilder("(select a.id " + "from alarm as a, alarm_definition as ad "
                    + "where ad.id = a.alarm_definition_id " + "  and ad.deleted_at is null "
                    + "  and ad.tenant_id = :tenantId ");

            if (alarmDefId != null) {
                sbWhere.append(" and ad.id = :alarmDefId ");
            }

            if (metricName != null) {

                sbWhere.append(" and a.id in (select distinct a.id from alarm as a "
                        + "inner join alarm_metric as am on am.alarm_id = a.id "
                        + "inner join metric_definition_dimensions as mdd "
                        + "  on mdd.id = am.metric_definition_dimensions_id "
                        + "inner join (select distinct id from metric_definition "
                        + "            where name = :metricName) as md "
                        + "  on md.id = mdd.metric_definition_id ");

                buildJoinClauseFor(metricDimensions, sbWhere);

                sbWhere.append(")");

            } else if (metricDimensions != null) {

                sbWhere.append(" and a.id in (select distinct a.id from alarm as a "
                        + "inner join alarm_metric as am on am.alarm_id = a.id "
                        + "inner join metric_definition_dimensions as mdd "
                        + "  on mdd.id = am.metric_definition_dimensions_id ");

                buildJoinClauseFor(metricDimensions, sbWhere);

                sbWhere.append(")");

            }

            if (state != null) {
                sbWhere.append(" and a.state = :state");
            }

            if (lifecycleState != null) {
                sbWhere.append(" and a.lifecycle_state = :lifecycleState");
            }

            if (link != null) {
                sbWhere.append(" and a.link = :link");
            }

            if (stateUpdatedStart != null) {
                sbWhere.append(" and a.state_updated_at >= :stateUpdatedStart");
            }

            if (offset != null) {
                sbWhere.append(" and a.id > :offset");
            }

            sbWhere.append(" order by a.id ASC ");

            if (enforceLimit && limit > 0) {
                sbWhere.append(" limit :limit");
            }

            sbWhere.append(")");

            String sql = String.format(FIND_ALARMS_SQL, sbWhere);

            try {
                query = session.createSQLQuery(sql);
            } catch (Exception e) {
                logger.error("Failed to bind query {}, error is {}", sql, e.getMessage());
                throw new RuntimeException("Failed to bind query", e);
            }

            query.setString("tenantId", tenantId);
            if (alarmDefId != null) {
                query.setString("alarmDefId", alarmDefId);
            }

            if (offset != null) {
                query.setString("offset", offset);
            }

            if (metricName != null) {
                query.setString("metricName", metricName);
            }

            if (state != null) {
                query.setString("state", state.name());
            }

            if (link != null) {
                query.setString("link", link);
            }

            if (lifecycleState != null) {
                query.setString("lifecycleState", lifecycleState);
            }

            if (stateUpdatedStart != null) {
                query.setDate("stateUpdatedStart", stateUpdatedStart.toDateTime(DateTimeZone.UTC).toDate());
            }

            if (enforceLimit && limit > 0) {
                query.setInteger("limit", limit + 1);
            }

            bindDimensionsToQuery(query, metricDimensions);

            List<Object[]> alarmList = (List<Object[]>) query.list();
            alarms = createAlarms(alarmList);

        } finally {
            if (session != null) {
                session.close();
            }
        }
        return alarms;
    }

    private List<Alarm> createAlarms(List<Object[]> alarmList) {
        List<Alarm> alarms = Lists.newLinkedList();

        String previousAlarmId = null;
        BinaryId previousDimensionSetId = null;
        List<MetricDefinition> alarmedMetrics = null;
        Map<String, String> dimensionMap = new HashMap<>();

        for (Object[] alarmRow : alarmList) {
            String alarmDefinitionId = (String) alarmRow[0];
            AlarmSeverity severity = Conversions.variantToEnum(alarmRow[1], AlarmSeverity.class);
            AlarmState alarmState = Conversions.variantToEnum(alarmRow[4], AlarmState.class);
            DateTime updatedTimestamp = Conversions.variantToDateTime(alarmRow[5]);
            DateTime createdTimestamp = Conversions.variantToDateTime(alarmRow[6]);
            BinaryId dimensionSetId = this.convertBinaryId(alarmRow[13]);
            DateTime stateUpdatedTimestamp = Conversions.variantToDateTime(alarmRow[12]);

            String alarm_definition_name = (String) alarmRow[2];
            String id = (String) alarmRow[3];

            String lifecycle_state = (String) alarmRow[10];
            String link = (String) alarmRow[11];

            String metric_name = (String) alarmRow[7];
            String dimension_name = (String) alarmRow[8];
            String dimension_value = (String) alarmRow[9];

            if (!id.equals(previousAlarmId)) {
                alarmedMetrics = new ArrayList<>();
                dimensionMap = Maps.newHashMap();
                alarmedMetrics.add(new MetricDefinition(metric_name, dimensionMap));

                alarms.add(new Alarm(id, alarmDefinitionId, alarm_definition_name, severity.name(), alarmedMetrics,
                        alarmState, lifecycle_state, link, stateUpdatedTimestamp, updatedTimestamp,
                        createdTimestamp));

                previousDimensionSetId = dimensionSetId;
            }

            if (!dimensionSetId.equals(previousDimensionSetId)) {
                dimensionMap = Maps.newHashMap();
                alarmedMetrics.add(new MetricDefinition(metric_name, dimensionMap));
            }

            dimensionMap.put(dimension_name, dimension_value);

            previousDimensionSetId = dimensionSetId;
            previousAlarmId = id;
        }
        return alarms;
    }

    private BinaryId convertBinaryId(final Object o) {
        final BinaryId dimensionSetId;
        if (o instanceof BinaryId) {
            dimensionSetId = (BinaryId) o;
        } else {
            dimensionSetId = new BinaryId((byte[]) o);
        }
        return dimensionSetId;
    }

    private void bindDimensionsToQuery(Query query, Map<String, String> dimensions) {

        if (dimensions != null) {
            int i = 0;
            for (Iterator<Map.Entry<String, String>> it = dimensions.entrySet().iterator(); it.hasNext(); i++) {
                Map.Entry<String, String> entry = it.next();
                query.setString("dname" + i, entry.getKey());
                query.setString("dvalue" + i, entry.getValue());
            }
        }
    }

    private void buildJoinClauseFor(Map<String, String> dimensions, StringBuilder sbJoin) {
        if (dimensions == null) {
            return;
        }
        for (int i = 0; i < dimensions.size(); i++) {
            final String indexStr = String.valueOf(i);
            sbJoin.append(" inner join metric_dimension md").append(indexStr).append(" on md").append(indexStr)
                    .append(".name = :dname").append(indexStr).append(" and md").append(indexStr)
                    .append(".value = :dvalue").append(indexStr).append(" and mdd.metric_dimension_set_id = md")
                    .append(indexStr).append(".dimension_set_id");
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Alarm findById(String tenantId, String id) {
        logger.trace(ORM_LOG_MARKER, "findById(...) entering");

        Session session = null;

        final String sql = String.format(FIND_ALARM_BY_ID_SQL, " and a.id = :id", "");
        List<Alarm> alarms = new LinkedList<>();
        try {
            session = sessionFactory.openSession();
            Query qAlarmDefinition = session.createQuery(sql).setString("tenantId", tenantId).setString("id", id);
            List<Object[]> alarmList = (List<Object[]>) qAlarmDefinition.list();

            if (alarmList.isEmpty()) {
                throw new EntityNotFoundException("No alarm exists for %s", id);
            }

            alarms = this.createAlarms(alarmList);

        } finally {
            if (session != null) {
                session.close();
            }
        }
        return alarms.get(0);
    }

    @Override
    public Alarm update(String tenantId, String id, AlarmState state, String lifecycleState, String link) {
        Session session = null;
        Alarm originalAlarm = null;
        Transaction tx = null;
        try {
            session = sessionFactory.openSession();
            tx = session.beginTransaction();
            originalAlarm = findById(tenantId, id);

            AlarmDb result = (AlarmDb) session.getNamedQuery(AlarmDb.Queries.FIND_BY_ID).setString("id", id)
                    .uniqueResult();

            if (!originalAlarm.getState().equals(state)) {
                result.setStateUpdatedAt(this.getUTCNow());
                result.setState(state);
            }

            result.setUpdatedAt(this.getUTCNow());
            result.setLink(link);
            result.setLifecycleState(lifecycleState);
            session.update(result);

            tx.commit();
            tx = null;
        } catch (Exception e) {
            this.rollbackIfNotNull(tx);
            throw e;
        } finally {
            if (session != null) {
                session.close();
            }
        }
        return originalAlarm;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, AlarmSubExpression> findAlarmSubExpressions(String alarmId) {
        Session session = null;
        final Map<String, AlarmSubExpression> subAlarms = Maps.newHashMap();
        logger.debug("AlarmSqlRepoImpl[findAlarmSubExpressions] called");
        try {

            session = sessionFactory.openSession();
            final List<SubAlarmDb> result = session.getNamedQuery(SubAlarmDb.Queries.BY_ALARM_ID)
                    .setString("id", alarmId).list();

            if (result != null) {
                for (SubAlarmDb row : result) {
                    subAlarms.put(row.getId(), AlarmSubExpression.of(row.getExpression()));
                }
            }
        } finally {
            if (session != null) {
                session.close();
            }
        }
        return subAlarms;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Map<String, AlarmSubExpression>> findAlarmSubExpressionsForAlarmDefinition(
            String alarmDefinitionId) {
        logger.trace(ORM_LOG_MARKER, "findAlarmSubExpressionsForAlarmDefinition(...) entering");

        Session session = null;
        Transaction tx = null;
        Map<String, Map<String, AlarmSubExpression>> subAlarms = Maps.newHashMap();

        try {
            session = sessionFactory.openSession();
            tx = session.beginTransaction();

            final Iterator<SubAlarmDb> rows = session.getNamedQuery(SubAlarmDb.Queries.BY_ALARMDEFINITION_ID)
                    .setString("id", alarmDefinitionId).setReadOnly(true).iterate();

            while (rows.hasNext()) {

                final SubAlarmDb row = rows.next();
                final String alarmId = (String) session.getIdentifier(row.getAlarm());

                Map<String, AlarmSubExpression> alarmMap = subAlarms.get(alarmId);
                if (alarmMap == null) {
                    alarmMap = Maps.newHashMap();
                    subAlarms.put(alarmId, alarmMap);
                }

                final String id = row.getId();
                final String expression = row.getExpression();
                alarmMap.put(id, AlarmSubExpression.of(expression));
            }

            tx.commit();
            tx = null;

        } catch (Exception exp) {
            this.rollbackIfNotNull(tx);
            throw exp;
        } finally {
            if (session != null) {
                session.close();
            }
        }

        return subAlarms;
    }

    @Override
    public AlarmCount getAlarmsCount(String tenantId, String alarmDefId, String metricName,
            Map<String, String> metricDimensions, AlarmState state, AlarmSeverity severity, String lifecycleState,
            String link, DateTime stateUpdatedStart, List<String> groupBy, String offset, int limit) {
        // Not Implemented
        return null;
    }
}