You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2014/09/03 14:35:40 UTC

git commit: AMBARI-7118. Alerts: populate incoming alert data to history and current (ncole)

Repository: ambari
Updated Branches:
  refs/heads/branch-alerts-dev 03a5be1e1 -> 0de98b020


AMBARI-7118. Alerts: populate incoming alert data to history and current (ncole)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/0de98b02
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/0de98b02
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/0de98b02

Branch: refs/heads/branch-alerts-dev
Commit: 0de98b0207b48007f39610f8dc8c0d13de6e7d92
Parents: 03a5be1
Author: Nate Cole <nc...@hortonworks.com>
Authored: Tue Sep 2 16:24:51 2014 -0400
Committer: Nate Cole <nc...@hortonworks.com>
Committed: Wed Sep 3 07:59:40 2014 -0400

----------------------------------------------------------------------
 .../ambari_agent/AlertSchedulerHandler.py       |   2 -
 .../python/ambari_agent/alerts/base_alert.py    |   2 +
 .../ambari/server/agent/HeartBeatHandler.java   |  13 ++
 .../apache/ambari/server/orm/dao/AlertsDAO.java |  14 ++
 .../server/orm/entities/AlertCurrentEntity.java |   4 +-
 .../org/apache/ambari/server/state/Alert.java   |  21 ++-
 .../server/state/cluster/AlertDataManager.java  | 110 +++++++++++
 .../server/orm/dao/AlertDefinitionDAOTest.java  |   2 +-
 .../ambari/server/orm/dao/AlertsDAOTest.java    |  14 ++
 .../state/cluster/AlertDataManagerTest.java     | 181 +++++++++++++++++++
 10 files changed, 358 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py b/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py
index cc5be92..e987dae 100644
--- a/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py
+++ b/ambari-agent/src/main/python/ambari_agent/AlertSchedulerHandler.py
@@ -57,8 +57,6 @@ class AlertSchedulerHandler():
       except:
         logger.critical("Could not create the cache directory {0}".format(cachedir))
         pass
-    
-
 
     self.__scheduler = Scheduler(AlertSchedulerHandler.APS_CONFIG)
     self.__in_minutes = in_minutes

http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py b/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
index bf8ada6..adb872a 100644
--- a/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
+++ b/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
@@ -20,6 +20,7 @@ limitations under the License.
 
 import logging
 import re
+import time
 import traceback
 
 logger = logging.getLogger()
@@ -78,6 +79,7 @@ class BaseAlert(object):
     data['cluster'] = self.cluster
     data['service'] = self._find_value('serviceName')
     data['component'] = self._find_value('componentName')
+    data['timestamp'] = long(time.time() * 1000)
     
     self.collector.put(self.cluster, data)
   

http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
index d0d5a13..492d832 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
@@ -60,6 +60,7 @@ import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.State;
 import org.apache.ambari.server.state.alert.AlertDefinition;
 import org.apache.ambari.server.state.alert.AlertDefinitionHash;
+import org.apache.ambari.server.state.cluster.AlertDataManager;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.host.HostHealthyHeartbeatEvent;
 import org.apache.ambari.server.state.host.HostRegistrationRequestEvent;
@@ -114,6 +115,9 @@ public class HeartBeatHandler {
 
   @Inject
   private AlertDefinitionHash alertDefinitionHash;
+  
+  @Inject
+  private AlertDataManager alertManager;
 
   private Map<String, Long> hostResponseIds = new ConcurrentHashMap<String, Long>();
 
@@ -232,6 +236,15 @@ public class HeartBeatHandler {
     if (null == hostname || null == heartbeat) {
       return;
     }
+    
+    if (null != heartbeat.getAlerts()) {
+      for (Alert alert : heartbeat.getAlerts()) {
+        if (null == alert.getHost())
+          alert.setHost(hostname);
+        Cluster cluster = clusterFsm.getCluster(alert.getCluster());
+        alertManager.add(cluster.getClusterId(), alert);
+      }
+    }
 
     for (Cluster cluster : clusterFsm.getClustersForHost(hostname)) {
       cluster.addAlerts(heartbeat.getNodeStatus().getAlerts());

http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertsDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertsDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertsDAO.java
index f2a5886..b43b5b1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertsDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertsDAO.java
@@ -239,6 +239,20 @@ public class AlertsDAO {
 
     return daoUtils.selectList(query);
   }
+  
+  @RequiresSession
+  public AlertCurrentEntity findCurrentByHostAndName(long clusterId, String hostName,
+      String alertName) {
+    
+    TypedQuery<AlertCurrentEntity> query = entityManagerProvider.get().createNamedQuery(
+        "AlertCurrentEntity.findByHostAndName", AlertCurrentEntity.class);
+
+    query.setParameter("clusterId", Long.valueOf(clusterId));
+    query.setParameter("hostName", hostName);
+    query.setParameter("definitionName", alertName);
+
+    return daoUtils.selectOne(query);
+  }
 
   /**
    * Removes alert history and current alerts for the specified alert defintiion

http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertCurrentEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertCurrentEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertCurrentEntity.java
index cde61f2..31b1673 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertCurrentEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertCurrentEntity.java
@@ -17,6 +17,7 @@
  */
 package org.apache.ambari.server.orm.entities;
 
+import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
@@ -48,6 +49,7 @@ import org.apache.ambari.server.state.MaintenanceState;
     @NamedQuery(name = "AlertCurrentEntity.findAll", query = "SELECT alert FROM AlertCurrentEntity alert"),
     @NamedQuery(name = "AlertCurrentEntity.findByService", query = "SELECT alert FROM AlertCurrentEntity alert JOIN alert.alertHistory history WHERE history.clusterId = :clusterId AND history.serviceName = :serviceName"),
     @NamedQuery(name = "AlertCurrentEntity.findByHost", query = "SELECT alert FROM AlertCurrentEntity alert JOIN alert.alertHistory history WHERE history.clusterId = :clusterId AND history.hostName = :hostName"),
+    @NamedQuery(name = "AlertCurrentEntity.findByHostAndName", query = "SELECT alert FROM AlertCurrentEntity alert JOIN alert.alertHistory history WHERE history.clusterId = :clusterId AND history.alertDefinition.definitionName = :definitionName AND history.hostName = :hostName"),
     @NamedQuery(name = "AlertCurrentEntity.removeByHistoryId", query = "DELETE FROM AlertCurrentEntity alert WHERE alert.alertHistory.alertId = :historyId"),
     @NamedQuery(name = "AlertCurrentEntity.removeByDefinitionId", query = "DELETE FROM AlertCurrentEntity alert WHERE alert.alertDefinition.definitionId = :definitionId") })
 public class AlertCurrentEntity {
@@ -70,7 +72,7 @@ public class AlertCurrentEntity {
   /**
    * Unidirectional one-to-one association to {@link AlertHistoryEntity}
    */
-  @OneToOne
+  @OneToOne(cascade = { CascadeType.PERSIST })
   @JoinColumn(name = "history_id", unique = true, nullable = false)
   private AlertHistoryEntity alertHistory;
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-server/src/main/java/org/apache/ambari/server/state/Alert.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Alert.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Alert.java
index e39b900..7b8aabd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Alert.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Alert.java
@@ -22,6 +22,7 @@ import org.codehaus.jackson.annotate.JsonProperty;
  * An alert represents a problem or notice for a cluster.
  */
 public class Alert {
+  private String cluster = null;
   private String name = null;
   private String instance = null;
   private String service = null;
@@ -30,6 +31,7 @@ public class Alert {
   private AlertState state = AlertState.UNKNOWN;
   private String label = null;
   private String text = null;
+  private long timestamp = 0L;
   
  
   /**
@@ -162,7 +164,23 @@ public class Alert {
   public void setState(AlertState state) {
     this.state = state;
   }
-
+  
+  @JsonProperty("timestamp")
+  public void setTimestamp(long ts) {
+    timestamp = ts;
+  }
+  
+  @JsonProperty("timestamp")
+  public long getTimestamp() {
+    return timestamp;
+  }
+  
+  /**
+   * @return
+   */
+  public String getCluster() {
+    return cluster;
+  }
   
   @Override
   public int hashCode() {
@@ -224,6 +242,7 @@ public class Alert {
     sb.append('}');
     return sb.toString();
   }
+
   
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/AlertDataManager.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/AlertDataManager.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/AlertDataManager.java
new file mode 100644
index 0000000..4670d3b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/AlertDataManager.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.state.cluster;
+
+import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
+import org.apache.ambari.server.orm.dao.AlertsDAO;
+import org.apache.ambari.server.orm.entities.AlertCurrentEntity;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
+import org.apache.ambari.server.state.Alert;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * The AlertManager is reponsible for tracking all alerts generated for a cluster. 
+ */
+@Singleton
+public class AlertDataManager {
+
+  @Inject
+  private AlertsDAO m_alertsDao;
+  @Inject
+  private AlertDefinitionDAO m_definitionDao;
+
+  
+  AlertDataManager() {
+  }
+  
+  /**
+   * Adds an alert.  Checks for a new state before creating a new history record.
+   * 
+   * @param clusterId the id for the cluster
+   * @param alert the alert to add
+   */
+  public void add(long clusterId, Alert alert) {
+    
+    AlertCurrentEntity current = m_alertsDao.findCurrentByHostAndName(clusterId,
+        alert.getHost(), alert.getName());
+    
+    if (null == current) {
+      AlertDefinitionEntity definition = m_definitionDao.findByName(clusterId,
+          alert.getName());
+      
+      AlertHistoryEntity history = createHistory(clusterId, definition, alert);
+      
+      current = new AlertCurrentEntity();
+      current.setAlertHistory(history);
+      current.setLatestTimestamp(Long.valueOf(alert.getTimestamp()));
+      current.setOriginalTimestamp(Long.valueOf(alert.getTimestamp()));
+      
+      m_alertsDao.create(current);
+      
+    } else if (alert.getState() == current.getAlertHistory().getAlertState()) {
+      current.setLatestTimestamp(Long.valueOf(alert.getTimestamp()));
+      
+      m_alertsDao.merge(current);
+    } else {
+      // insert history, update current
+      AlertHistoryEntity history = createHistory(clusterId,
+          current.getAlertHistory().getAlertDefinition(), alert);
+      
+      current.setAlertHistory(history);
+      current.setLatestTimestamp(Long.valueOf(alert.getTimestamp()));
+      current.setOriginalTimestamp(Long.valueOf(alert.getTimestamp()));
+      
+      m_alertsDao.merge(current);
+    }
+    
+  }
+  /**
+   * Convenience to create a new alert.
+   * @param clusterId the cluster id
+   * @param definition the definition
+   * @param alert the alert data
+   * @return the new history record
+   */
+  private AlertHistoryEntity createHistory(long clusterId, AlertDefinitionEntity definition, Alert alert) {
+    AlertHistoryEntity history = new AlertHistoryEntity();
+    history.setAlertDefinition(definition);
+    history.setAlertInstance(alert.getInstance());
+    history.setAlertLabel(alert.getLabel());
+    history.setAlertState(alert.getState());
+    history.setAlertText(alert.getText());
+    history.setAlertTimestamp(Long.valueOf(alert.getTimestamp()));
+    history.setClusterId(Long.valueOf(clusterId));
+    history.setComponentName(alert.getComponent());
+    history.setHostName(alert.getHost());
+    history.setServiceName(alert.getService());
+    
+    return history;
+  }
+  
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
index a7cc066..b8af7c1 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
@@ -287,7 +287,7 @@ public class AlertDefinitionDAOTest {
     history.setAlertState(AlertState.OK);
     history.setAlertText("Alert Text");
     history.setAlertTimestamp(calendar.getTimeInMillis());
-    alertsDao.create(history);
+//    alertsDao.create(history);
 
     AlertCurrentEntity current = new AlertCurrentEntity();
     current.setAlertHistory(history);

http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java
index caeada1..4500c1c 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java
@@ -21,6 +21,7 @@ package org.apache.ambari.server.orm.dao;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -104,6 +105,7 @@ public class AlertsDAOTest {
         history.setAlertLabel(definition.getDefinitionName() + " " + i);
         history.setAlertText(definition.getDefinitionName() + " " + i);
         history.setAlertTimestamp(calendar.getTimeInMillis());
+        history.setHostName("h1");
 
         history.setAlertState(AlertState.OK);
         if (i == 0 || i == 5) {
@@ -261,4 +263,16 @@ public class AlertsDAOTest {
     assertNotNull(history);
     assertEquals(0, history.size());
   }
+  
+  @Test
+  public void testFindCurrentByHostAndName() throws Exception {
+    AlertCurrentEntity entity = dao.findCurrentByHostAndName(clusterId.longValue(), "h2", "Alert Definition 1");
+    assertNull(entity);
+    
+    entity = dao.findCurrentByHostAndName(clusterId.longValue(), "h1", "Alert Definition 1");
+    
+    assertNotNull(entity);
+    assertNotNull(entity.getAlertHistory());
+    assertNotNull(entity.getAlertHistory().getAlertDefinition());
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/0de98b02/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/AlertDataManagerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/AlertDataManagerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/AlertDataManagerTest.java
new file mode 100644
index 0000000..eae1de6
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/AlertDataManagerTest.java
@@ -0,0 +1,181 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.state.cluster;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.ambari.server.orm.GuiceJpaInitializer;
+import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
+import org.apache.ambari.server.orm.OrmTestHelper;
+import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
+import org.apache.ambari.server.orm.dao.AlertsDAO;
+import org.apache.ambari.server.orm.entities.AlertCurrentEntity;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
+import org.apache.ambari.server.state.Alert;
+import org.apache.ambari.server.state.AlertState;
+import org.apache.ambari.server.state.alert.Scope;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.persist.PersistService;
+
+/**
+ * Tests the alert manager.
+ */
+public class AlertDataManagerTest {
+  
+  private static final String ALERT_DEFINITION = "Alert Definition 1";
+  private static final String SERVICE = "service1";
+  private static final String COMPONENT = "component1";
+  private static final String HOST1 = "h1";
+  private static final String HOST2 = "h2";
+  private static final String ALERT_LABEL = "My Label";
+  
+  private Long clusterId;
+  private Injector injector;
+  private OrmTestHelper helper;
+  private AlertsDAO dao;
+  
+  @Before
+  public void setup() throws Exception {
+    injector = Guice.createInjector(new InMemoryDefaultTestModule());
+    injector.getInstance(GuiceJpaInitializer.class);
+    helper = injector.getInstance(OrmTestHelper.class);
+    clusterId = helper.createCluster();
+    dao = injector.getInstance(AlertsDAO.class);
+    AlertDefinitionDAO definitionDao = injector.getInstance(AlertDefinitionDAO.class);
+
+    // create 5 definitions
+    for (int i = 0; i < 5; i++) {
+      AlertDefinitionEntity definition = new AlertDefinitionEntity();
+      definition.setDefinitionName("Alert Definition " + i);
+      definition.setServiceName("Service " + i);
+      definition.setComponentName(null);
+      definition.setClusterId(clusterId);
+      definition.setHash(UUID.randomUUID().toString());
+      definition.setScheduleInterval(Integer.valueOf(60));
+      definition.setScope(Scope.SERVICE);
+      definition.setSource("Source " + i);
+      definition.setSourceType("SCRIPT");
+      definitionDao.create(definition);
+    }
+  
+  }
+  
+  @After
+  public void teardown() {
+    injector.getInstance(PersistService.class).stop();
+    injector = null;
+  }
+  
+  @Test
+  public void testAlertRecords() {
+    
+    Alert alert1 = new Alert(ALERT_DEFINITION, null, SERVICE, COMPONENT, HOST1, AlertState.OK);
+    alert1.setLabel(ALERT_LABEL);
+    alert1.setText("Component component1 is OK");
+    alert1.setTimestamp(1L);
+
+    Alert alert2 = new Alert(ALERT_DEFINITION, null, SERVICE, COMPONENT, HOST2, AlertState.CRITICAL);
+    alert2.setLabel(ALERT_LABEL);
+    alert2.setText("Component component2 is not OK");
+    
+    AlertDataManager am = injector.getInstance(AlertDataManager.class);
+    
+    am.add(clusterId.longValue(), alert1);
+    am.add(clusterId.longValue(), alert2);
+    
+    List<AlertCurrentEntity> allCurrent = dao.findCurrentByService(clusterId.longValue(), SERVICE);
+    assertEquals(2, allCurrent.size());
+    
+    List<AlertHistoryEntity> allHistory = dao.findAll(clusterId.longValue());
+    assertEquals(2, allHistory.size());
+    
+    AlertCurrentEntity current = dao.findCurrentByHostAndName(clusterId.longValue(), HOST1, ALERT_DEFINITION);
+    assertNotNull(current);
+    assertEquals(HOST1, current.getAlertHistory().getHostName());
+    assertEquals(ALERT_DEFINITION, current.getAlertHistory().getAlertDefinition().getDefinitionName());
+    assertEquals(ALERT_LABEL, current.getAlertHistory().getAlertLabel());
+    assertEquals("Component component1 is OK", current.getAlertHistory().getAlertText());
+    assertEquals(current.getAlertHistory().getAlertState(), AlertState.OK);
+    assertEquals(1L, current.getOriginalTimestamp().longValue());
+    assertEquals(1L, current.getLatestTimestamp().longValue());
+    
+    Long currentId = current.getAlertId();
+    Long historyId = current.getAlertHistory().getAlertId();
+    
+    // no new history since the state is the same
+    Alert alert3 = new Alert(ALERT_DEFINITION, null, SERVICE, COMPONENT, HOST1, AlertState.OK);
+    alert3.setLabel(ALERT_LABEL);
+    alert3.setText("Component component1 is OK");
+    alert3.setTimestamp(2L);
+    am.add(clusterId.longValue(), alert3);
+    
+    current = dao.findCurrentByHostAndName(clusterId.longValue(), HOST1, ALERT_DEFINITION);
+    assertNotNull(current);
+    assertEquals(currentId, current.getAlertId());
+    assertEquals(historyId, current.getAlertHistory().getAlertId());
+    assertEquals(HOST1, current.getAlertHistory().getHostName());
+    assertEquals(ALERT_DEFINITION, current.getAlertHistory().getAlertDefinition().getDefinitionName());
+    assertEquals(ALERT_LABEL, current.getAlertHistory().getAlertLabel());
+    assertEquals("Component component1 is OK", current.getAlertHistory().getAlertText());
+    assertEquals(current.getAlertHistory().getAlertState(), AlertState.OK);
+    assertEquals(1L, current.getOriginalTimestamp().longValue());
+    assertEquals(2L, current.getLatestTimestamp().longValue());
+   
+    allCurrent = dao.findCurrentByService(clusterId.longValue(), SERVICE);
+    assertEquals(2, allCurrent.size());
+    
+    allHistory = dao.findAll(clusterId.longValue());
+    assertEquals(2, allHistory.size());
+    
+    // change to warning
+    Alert alert4 = new Alert(ALERT_DEFINITION, null, SERVICE, COMPONENT, HOST1, AlertState.WARNING);
+    alert4.setLabel(ALERT_LABEL);
+    alert4.setText("Component component1 is about to go down");
+    alert4.setTimestamp(3L);
+    am.add(clusterId.longValue(), alert4);
+
+    current = dao.findCurrentByHostAndName(clusterId.longValue(), HOST1, ALERT_DEFINITION);
+    assertNotNull(current);
+    assertEquals(current.getAlertId(), currentId);
+    assertFalse(historyId.equals(current.getAlertHistory().getAlertId()));    
+    assertEquals(HOST1, current.getAlertHistory().getHostName());
+    assertEquals(ALERT_DEFINITION, current.getAlertHistory().getAlertDefinition().getDefinitionName());
+    assertEquals(ALERT_LABEL, current.getAlertHistory().getAlertLabel());
+    assertEquals("Component component1 is about to go down", current.getAlertHistory().getAlertText());
+    assertEquals(current.getAlertHistory().getAlertState(), AlertState.WARNING);
+    assertEquals(3L, current.getOriginalTimestamp().longValue());
+    assertEquals(3L, current.getLatestTimestamp().longValue());
+
+    allCurrent = dao.findCurrentByService(clusterId.longValue(), SERVICE);
+    assertEquals(2, allCurrent.size());
+    
+    allHistory = dao.findAll(clusterId.longValue());
+    assertEquals(3, allHistory.size());
+  }
+}