You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2016/08/30 03:36:45 UTC
ambari git commit: AMBARI-18271 - Multiple Aggregate Alerts For
JournalNode Exist With Some Being Incorrect (jonathanhurley)
Repository: ambari
Updated Branches:
refs/heads/trunk 1eba77a93 -> 580e920a1
AMBARI-18271 - Multiple Aggregate Alerts For JournalNode Exist With Some Being Incorrect (jonathanhurley)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/580e920a
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/580e920a
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/580e920a
Branch: refs/heads/trunk
Commit: 580e920a145cebf0e3899bc8ea4e3fc4b4d367dc
Parents: 1eba77a
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Mon Aug 29 12:30:51 2016 -0400
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Mon Aug 29 23:35:47 2016 -0400
----------------------------------------------------------------------
.../listeners/alerts/AlertReceivedListener.java | 109 +++++++++++++------
.../server/orm/entities/AlertCurrentEntity.java | 36 ++++--
.../orm/entities/AlertDefinitionEntity.java | 37 +++++--
.../server/orm/entities/AlertHistoryEntity.java | 59 ++++++++--
.../orm/entities/AlertCurrentEntityTest.java | 64 +++++++++++
.../orm/entities/AlertDefinitionEntityTest.java | 69 ++++++++++++
.../orm/entities/AlertHistoryEntityTest.java | 89 +++++++++++++++
.../state/alerts/AlertReceivedListenerTest.java | 45 ++++++++
8 files changed, 447 insertions(+), 61 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/580e920a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java
index 2dcf1d6..907e4d8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java
@@ -19,6 +19,8 @@ package org.apache.ambari.server.events.listeners.alerts;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.locks.Lock;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.EagerSingleton;
@@ -52,6 +54,7 @@ import org.slf4j.LoggerFactory;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
+import com.google.common.util.concurrent.Striped;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -101,6 +104,12 @@ public class AlertReceivedListener {
private AlertEventPublisher m_alertEventPublisher;
/**
+ * Used for ensuring that creation of {@link AlertCurrentEntity} instances has fine-grain
+ * locks to prevent duplicates.
+ */
+ private Striped<Lock> creationLocks = Striped.lazyWeakLock(100);
+
+ /**
* Constructor.
*
* @param publisher
@@ -129,7 +138,7 @@ public class AlertReceivedListener {
// many transactions/commits
List<Alert> alerts = event.getAlerts();
- List<AlertCurrentEntity> toCreate = new ArrayList<>();
+ // these can be wrapped in their own transaction
List<AlertCurrentEntity> toMerge = new ArrayList<>();
List<AlertCurrentEntity> toCreateHistoryAndMerge = new ArrayList<>();
@@ -173,47 +182,63 @@ public class AlertReceivedListener {
AlertCurrentEntity current;
AlertState alertState = alert.getState();
- if (StringUtils.isBlank(alert.getHostName()) || definition.isHostIgnored()) {
- current = m_alertsDao.findCurrentByNameNoHost(clusterId, alert.getName());
- } else {
- current = m_alertsDao.findCurrentByHostAndName(clusterId, alert.getHostName(),
- alert.getName());
- }
+ // attempt to lookup the current alert
+ current = getCurrentEntity(clusterId, alert, definition);
+
+ // if it doesn't exist then we must create it, ensuring that two or more
+ // aren't created from other threads
+ if( null == current ){
- if (null == current) {
- // if there is no current alert and the state is skipped, then simple
+ // if there is no current alert and the state is skipped, then simply
// skip over this one as there is nothing to update in the databse
if (alertState == AlertState.SKIPPED) {
continue;
}
- AlertHistoryEntity history = createHistory(clusterId, definition, alert);
+ // create a key out of the cluster/definition name/host (possibly null)
+ int key = Objects.hash(clusterId, alert.getName(), alert.getHostName());
+ Lock lock = creationLocks.get(key);
+ lock.lock();
- // this new alert must reflect the correct MM state for the
- // service/component/host
- MaintenanceState maintenanceState = MaintenanceState.OFF;
+ // attempt to lookup the current alert again to ensure that a previous
+ // thread didn't already create it
try {
- maintenanceState = m_maintenanceStateHelper.get().getEffectiveState(clusterId, alert);
- } catch (Exception exception) {
- LOG.error("Unable to determine the maintenance mode state for {}, defaulting to OFF",
- alert, exception);
- }
+ // if it's not null anymore, then there's no work to do here
+ current = getCurrentEntity(clusterId, alert, definition);
+ if( null != current ) {
+ continue;
+ }
- current = new AlertCurrentEntity();
- current.setMaintenanceState(maintenanceState);
- current.setAlertHistory(history);
- current.setLatestTimestamp(alert.getTimestamp());
- current.setOriginalTimestamp(alert.getTimestamp());
+ // the current alert is still null, so go through and create it
+ AlertHistoryEntity history = createHistory(clusterId, definition, alert);
+
+ // this new alert must reflect the correct MM state for the
+ // service/component/host
+ MaintenanceState maintenanceState = MaintenanceState.OFF;
+ try {
+ maintenanceState = m_maintenanceStateHelper.get().getEffectiveState(clusterId, alert);
+ } catch (Exception exception) {
+ LOG.error("Unable to determine the maintenance mode state for {}, defaulting to OFF",
+ alert, exception);
+ }
- // brand new alert instances being received are always HARD
- current.setFirmness(AlertFirmness.HARD);
+ current = new AlertCurrentEntity();
+ current.setMaintenanceState(maintenanceState);
+ current.setAlertHistory(history);
+ current.setLatestTimestamp(alert.getTimestamp());
+ current.setOriginalTimestamp(alert.getTimestamp());
- // store the entity for creation later
- toCreate.add(current);
+ // brand new alert instances being received are always HARD
+ current.setFirmness(AlertFirmness.HARD);
- // create the event to fire later
- alertEvents.add(new InitialAlertEvent(clusterId, alert, current));
+ m_alertsDao.create(current);
+ // create the event to fire later
+ alertEvents.add(new InitialAlertEvent(clusterId, alert, current));
+ } finally {
+ // release the lock for this alert
+ lock.unlock();
+ }
} else if (alertState == current.getAlertHistory().getAlertState()
|| alertState == AlertState.SKIPPED) {
@@ -319,7 +344,7 @@ public class AlertReceivedListener {
// invokes the EntityManager create/merge on various entities in a single
// transaction
- saveEntities(toCreate, toMerge, toCreateHistoryAndMerge);
+ saveEntities(toMerge, toCreateHistoryAndMerge);
// broadcast events
for (AlertEvent eventToFire : alertEvents) {
@@ -343,18 +368,30 @@ public class AlertReceivedListener {
}
/**
+ * Gets the {@link AlertCurrentEntity} which cooresponds to the new alert being received, if any.
+ *
+ * @param clusterId the ID of the cluster.
+ * @param alert the alert being received (not {@code null}).
+ * @param definition the {@link AlertDefinitionEntity} for the alert being received (not {@code null}).
+ * @return the existing current alert or {@code null} for none.
+ */
+ private AlertCurrentEntity getCurrentEntity(long clusterId, Alert alert, AlertDefinitionEntity definition){
+ if (StringUtils.isBlank(alert.getHostName()) || definition.isHostIgnored()) {
+ return m_alertsDao.findCurrentByNameNoHost(clusterId, alert.getName());
+ } else {
+ return m_alertsDao.findCurrentByHostAndName(clusterId, alert.getHostName(),
+ alert.getName());
+ }
+ }
+
+ /**
* Saves alert and alert history entities in single transaction
- * @param toCreate - new alerts, create alert and history
* @param toMerge - merge alert only
* @param toCreateHistoryAndMerge - create new history, merge alert
*/
@Transactional
- void saveEntities(List<AlertCurrentEntity> toCreate, List<AlertCurrentEntity> toMerge,
+ void saveEntities(List<AlertCurrentEntity> toMerge,
List<AlertCurrentEntity> toCreateHistoryAndMerge) {
- for (AlertCurrentEntity entity : toCreate) {
- m_alertsDao.create(entity);
- }
-
for (AlertCurrentEntity entity : toMerge) {
m_alertsDao.merge(entity, m_configuration.isAlertCacheEnabled());
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/580e920a/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 b30b100..651e1a1 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,8 @@
*/
package org.apache.ambari.server.orm.entities;
+import java.util.Objects;
+
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -36,6 +38,7 @@ import javax.persistence.TableGenerator;
import org.apache.ambari.server.state.AlertFirmness;
import org.apache.ambari.server.state.AlertState;
import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.commons.lang.builder.EqualsBuilder;
/**
* The {@link AlertCurrentEntity} class represents the most recently received an
@@ -331,7 +334,13 @@ public class AlertCurrentEntity {
}
/**
- *
+ * Gets the equality to another alert based on the following criteria:
+ * <ul>
+ * <li>{@link #alertId}
+ * <li>{@link #alertHistory}
+ * </ul>
+ * <p/>
+ * {@inheritDoc}
*/
@Override
public boolean equals(Object object) {
@@ -344,21 +353,30 @@ public class AlertCurrentEntity {
}
AlertCurrentEntity that = (AlertCurrentEntity) object;
+ EqualsBuilder equalsBuilder = new EqualsBuilder();
- if (alertId != null ? !alertId.equals(that.alertId) : that.alertId != null) {
- return false;
- }
-
- return true;
+ equalsBuilder.append(alertId, that.alertId);
+ equalsBuilder.append(alertHistory, that.alertHistory);
+ return equalsBuilder.isEquals();
}
/**
- *
+ * Generates a hash for the current alert based on the following criteria:
+ * <ul>
+ * <li>{@link #alertId}
+ * <li>{@link #alertHistory}
+ * </ul>
+ * <p/>
+ * For new alerts, the associated {@link AlertHistoryEntity} may not be
+ * persisted yet. This will rely on the
+ * {@link AlertHistoryEntity#equals(Object)} and
+ * {@link AlertHistoryEntity#hashCode()} being correct.
+ * <p/>
+ * {@inheritDoc}
*/
@Override
public int hashCode() {
- int result = null != alertId ? alertId.hashCode() : 0;
- return result;
+ return Objects.hash(alertId, alertHistory);
}
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/580e920a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java
index 638646c..0eebf4b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java
@@ -20,6 +20,7 @@ package org.apache.ambari.server.orm.entities;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Objects;
import java.util.Set;
import javax.persistence.Basic;
@@ -593,7 +594,17 @@ public class AlertDefinitionEntity {
}
/**
- *
+ * Gets the equality to another historical alert entry based on the following criteria:
+ * <ul>
+ * <li>{@link #definitionId}
+ * <li>{@link #clusterId}
+ * <li>{@link #definitionName}
+ * </ul>
+ * <p/>
+ * However, since we're guaranteed that {@link #definitionId} is unique among persisted entities, we
+ * can return the hashCode based on this value if it is set.
+ * <p/>
+ * {@inheritDoc}
*/
@Override
public boolean equals(Object object) {
@@ -607,21 +618,31 @@ public class AlertDefinitionEntity {
AlertDefinitionEntity that = (AlertDefinitionEntity) object;
- if (definitionId != null ? !definitionId.equals(that.definitionId)
- : that.definitionId != null) {
- return false;
+ // use the unique ID if it exists
+ if( null != definitionId ){
+ return Objects.equals(definitionId, that.definitionId);
}
- return true;
+ return Objects.equals(definitionId, that.definitionId) &&
+ Objects.equals(clusterId, that.clusterId) &&
+ Objects.equals(definitionName, that.definitionName);
}
/**
- *
+ * Gets a hash to uniquely identify this alert definition. Since we're
+ * guaranteed that {@link #definitionId} is unique among persisted entities,
+ * we can return the hashCode based on this value if it is set.
+ * <p/>
+ * {@inheritDoc}
*/
@Override
public int hashCode() {
- int result = null != definitionId ? definitionId.hashCode() : 0;
- return result;
+ // use the unique ID if it exists
+ if( null != definitionId ){
+ return definitionId.hashCode();
+ }
+
+ return Objects.hash(definitionId, clusterId, definitionName);
}
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/580e920a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertHistoryEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertHistoryEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertHistoryEntity.java
index 8bc4b99..958740a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertHistoryEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertHistoryEntity.java
@@ -17,6 +17,8 @@
*/
package org.apache.ambari.server.orm.entities;
+import java.util.Objects;
+
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
@@ -331,7 +333,7 @@ public class AlertHistoryEntity {
*/
public void setAlertDefinition(AlertDefinitionEntity alertDefinition) {
this.alertDefinition = alertDefinition;
- this.alertDefinitionId = alertDefinition.getDefinitionId();
+ alertDefinitionId = alertDefinition.getDefinitionId();
}
/**
@@ -351,7 +353,25 @@ public class AlertHistoryEntity {
}
/**
- *
+ * Gets the equality to another historical alert entry based on the following criteria:
+ * <ul>
+ * <li>{@link #alertId}
+ * <li>{@link #clusterId}
+ * <li>{@link #alertInstance}
+ * <li>{@link #alertLabel}
+ * <li>{@link #alertState}
+ * <li>{@link #alertText}
+ * <li>{@link #alertTimestamp}
+ * <li>{@link #serviceName}
+ * <li>{@link #componentName}
+ * <li>{@link #hostName}
+ * <li>{@link #alertDefinition}
+ * </ul>
+ * <p/>
+ * However, since we're guaranteed that {@link #alertId} is unique among persisted entities, we
+ * can return the hashCode based on this value if it is set.
+ * <p/>
+ * {@inheritDoc}
*/
@Override
public boolean equals(Object object) {
@@ -365,20 +385,43 @@ public class AlertHistoryEntity {
AlertHistoryEntity that = (AlertHistoryEntity) object;
- if (alertId != null ? !alertId.equals(that.alertId) : that.alertId != null) {
- return false;
+ // use the unique ID if it exists
+ if( null != alertId ){
+ return Objects.equals(alertId, that.alertId);
}
- return true;
+ return Objects.equals(alertId, that.alertId) &&
+ Objects.equals(clusterId, that.clusterId) &&
+ Objects.equals(alertInstance, that.alertInstance) &&
+ Objects.equals(alertLabel, that.alertLabel) &&
+ Objects.equals(alertState, that.alertState) &&
+ Objects.equals(alertText, that.alertText) &&
+ Objects.equals(alertTimestamp, that.alertTimestamp) &&
+ Objects.equals(serviceName, that.serviceName) &&
+ Objects.equals(componentName, that.componentName) &&
+ Objects.equals(hostName, that.hostName) &&
+ Objects.equals(alertDefinition, that.alertDefinition);
}
/**
- *
+ * Gets a hash to uniquely identify this historical alert instance. Since historical entries
+ * have no real uniqueness properties, we need to rely on a combination of the fields of this
+ * entity.
+ * <p/>
+ * However, since we're guaranteed that {@link #alertId} is unique among persisted entities, we
+ * can return the hashCode based on this value if it is set.
+ * <p/>
+ * {@inheritDoc}
*/
@Override
public int hashCode() {
- int result = null != alertId ? alertId.hashCode() : 0;
- return result;
+ // use the unique ID if it exists
+ if( null != alertId ){
+ return alertId.hashCode();
+ }
+
+ return Objects.hash(alertId, clusterId, alertInstance, alertLabel, alertState, alertText,
+ alertTimestamp, serviceName, componentName, hostName, alertDefinition);
}
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/580e920a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertCurrentEntityTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertCurrentEntityTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertCurrentEntityTest.java
new file mode 100644
index 0000000..73d2c7e
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertCurrentEntityTest.java
@@ -0,0 +1,64 @@
+/**
+ * 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.orm.entities;
+
+import java.util.Objects;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests methods on {@link AlertCurrentEntity}.
+ */
+public class AlertCurrentEntityTest {
+
+ /**
+ * Tests {@link AlertCurrentEntity#hashCode()} and {@link AlertCurrentEntity#equals(Object)}
+ */
+ @Test
+ public void testHashCodeAndEquals(){
+ AlertHistoryEntity history1 = new AlertHistoryEntity();
+ AlertHistoryEntity history2 = new AlertHistoryEntity();
+ history1.setAlertId(1L);
+ history2.setAlertId(2L);
+
+ AlertCurrentEntity current1 = new AlertCurrentEntity();
+ AlertCurrentEntity current2 = new AlertCurrentEntity();
+
+ Assert.assertEquals(current1.hashCode(), current2.hashCode());
+ Assert.assertTrue(Objects.equals(current1, current2));
+
+ current1.setAlertHistory(history1);
+ current2.setAlertHistory(history2);
+ Assert.assertNotSame(current1.hashCode(), current2.hashCode());
+ Assert.assertFalse(Objects.equals(current1, current2));
+
+ current2.setAlertHistory(history1);
+ Assert.assertEquals(current1.hashCode(), current2.hashCode());
+ Assert.assertTrue(Objects.equals(current1, current2));
+
+ current1.setAlertId(1L);
+ current2.setAlertId(2L);
+ Assert.assertNotSame(current1.hashCode(), current2.hashCode());
+ Assert.assertFalse(Objects.equals(current1, current2));
+
+ current2.setAlertId(1L);
+ Assert.assertEquals(current1.hashCode(), current2.hashCode());
+ Assert.assertTrue(Objects.equals(current1, current2));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/580e920a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntityTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntityTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntityTest.java
new file mode 100644
index 0000000..fc4bc90
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntityTest.java
@@ -0,0 +1,69 @@
+/**
+ * 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.orm.entities;
+
+import java.util.Objects;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests methods on {@link AlertDefinitionEntity}.
+ */
+public class AlertDefinitionEntityTest {
+
+ /**
+ * Tests {@link AlertDefinitionEntity#hashCode()} and {@link AlertDefinitionEntity#equals(Object)}
+ */
+ @Test
+ public void testHashCodeAndEquals(){
+ AlertDefinitionEntity definition1 = new AlertDefinitionEntity();
+ AlertDefinitionEntity definition2 = new AlertDefinitionEntity();
+
+ Assert.assertEquals(definition1.hashCode(), definition2.hashCode());
+ Assert.assertTrue(Objects.equals(definition1, definition2));
+
+ definition1.setClusterId(1L);
+ definition2.setClusterId(1L);
+ Assert.assertEquals(definition1.hashCode(), definition2.hashCode());
+ Assert.assertTrue(Objects.equals(definition1, definition2));
+
+ definition1.setDefinitionName("definition-name");
+ definition2.setDefinitionName("definition-name");
+ Assert.assertEquals(definition1.hashCode(), definition2.hashCode());
+ Assert.assertTrue(Objects.equals(definition1, definition2));
+
+ definition2.setDefinitionName("definition-name-foo");
+ Assert.assertNotSame(definition1.hashCode(), definition2.hashCode());
+ Assert.assertFalse(Objects.equals(definition1, definition2));
+
+ definition2.setDefinitionName("definition-name");
+ definition2.setClusterId(2L);
+ Assert.assertNotSame(definition1.hashCode(), definition2.hashCode());
+ Assert.assertFalse(Objects.equals(definition1, definition2));
+
+ definition2.setClusterId(1L);
+ definition1.setDefinitionId(1L);
+ Assert.assertNotSame(definition1.hashCode(), definition2.hashCode());
+ Assert.assertFalse(Objects.equals(definition1, definition2));
+
+ definition2.setDefinitionId(1L);
+ Assert.assertEquals(definition1.hashCode(), definition2.hashCode());
+ Assert.assertTrue(Objects.equals(definition1, definition2));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/580e920a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertHistoryEntityTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertHistoryEntityTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertHistoryEntityTest.java
new file mode 100644
index 0000000..beac248
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/AlertHistoryEntityTest.java
@@ -0,0 +1,89 @@
+/**
+ * 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.orm.entities;
+
+import java.util.Objects;
+
+import org.apache.ambari.server.state.AlertState;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests methods on {@link AlertHistoryEntity}.
+ */
+public class AlertHistoryEntityTest {
+
+ /**
+ * Tests {@link AlertHistoryEntity#hashCode()} and {@link AlertHistoryEntity#equals(Object)}
+ */
+ @Test
+ public void testHashCodeAndEquals(){
+ AlertDefinitionEntity definition1 = new AlertDefinitionEntity();
+ definition1.setDefinitionId(1L);
+ AlertDefinitionEntity definition2 = new AlertDefinitionEntity();
+ definition2.setDefinitionId(2L);
+
+ AlertHistoryEntity history1 = new AlertHistoryEntity();
+ AlertHistoryEntity history2 = new AlertHistoryEntity();
+
+ Assert.assertEquals(history1.hashCode(), history2.hashCode());
+ Assert.assertTrue(Objects.equals(history1, history2));
+
+ history1.setAlertDefinition(definition1);
+ history2.setAlertDefinition(definition2);
+ Assert.assertNotSame(history1.hashCode(), history2.hashCode());
+ Assert.assertFalse(Objects.equals(history1, history2));
+
+ history2.setAlertDefinition(definition1);
+ Assert.assertEquals(history1.hashCode(), history2.hashCode());
+ Assert.assertTrue(Objects.equals(history1, history2));
+
+ history1.setAlertLabel("label");
+ history1.setAlertState(AlertState.OK);
+ history1.setAlertText("text");
+ history1.setAlertTimestamp(1L);
+ history1.setClusterId(1L);
+ history1.setComponentName("component");
+ history1.setServiceName("service");
+ history1.setHostName("host");
+ history2.setAlertLabel("label");
+ history2.setAlertState(AlertState.OK);
+ history2.setAlertText("text");
+ history2.setAlertTimestamp(1L);
+ history2.setClusterId(1L);
+ history2.setComponentName("component");
+ history2.setServiceName("service");
+ history2.setHostName("host");
+ Assert.assertEquals(history1.hashCode(), history2.hashCode());
+ Assert.assertTrue(Objects.equals(history1, history2));
+
+ history2.setAlertState(AlertState.CRITICAL);
+ Assert.assertNotSame(history1.hashCode(), history2.hashCode());
+ Assert.assertFalse(Objects.equals(history1, history2));
+
+ history2.setAlertState(AlertState.OK);
+ history1.setAlertId(1L);
+ history2.setAlertId(1L);
+ Assert.assertEquals(history1.hashCode(), history2.hashCode());
+ Assert.assertTrue(Objects.equals(history1, history2));
+
+ history2.setAlertId(2L);
+ Assert.assertNotSame(history1.hashCode(), history2.hashCode());
+ Assert.assertFalse(Objects.equals(history1, history2));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/580e920a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java
index 7bf11e3..7aef175 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java
@@ -19,6 +19,7 @@ package org.apache.ambari.server.state.alerts;
import static org.junit.Assert.assertEquals;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -887,4 +888,48 @@ public class AlertReceivedListenerTest {
assertEquals(3, (long) allCurrent.get(0).getOccurrences());
assertEquals(AlertFirmness.HARD, allCurrent.get(0).getFirmness());
}
+
+ /**
+ * Tests that multiple threads can't create duplicate new alerts. This will
+ * spawn several threads, each one trying to create the same alert.
+ */
+ @Test
+ public void testMultipleNewAlertEvents() throws Exception {
+ assertEquals(0, m_dao.findCurrent().size());
+
+ final String definitionName = ALERT_DEFINITION + "1";
+ List<Thread> threads = new ArrayList<>();
+ final AlertReceivedListener listener = m_injector.getInstance(AlertReceivedListener.class);
+
+ // spawn a bunch of concurrent threasd which will try to create teh same
+ // alert over and over
+ for (int i = 0; i < 10; i++) {
+ Thread thread = new Thread() {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void run() {
+ Alert alert = new Alert(definitionName, null, "HDFS", null, HOST1, AlertState.OK);
+ alert.setCluster(m_cluster.getClusterName());
+ alert.setLabel(ALERT_LABEL);
+ alert.setText("HDFS is OK ");
+ alert.setTimestamp(System.currentTimeMillis());
+
+ final AlertReceivedEvent event = new AlertReceivedEvent(m_cluster.getClusterId(), alert);
+ listener.onAlertEvent(event);
+ }
+ };
+
+ threads.add(thread);
+ thread.start();
+ }
+
+ // wait for threads
+ for (Thread thread : threads) {
+ thread.join();
+ }
+
+ assertEquals(1, m_dao.findCurrent().size());
+ }
}