Browse Source

AMBARI-7734 - Alerts: Create REST API Endpoint for Alert History (jonathanhurley)

Jonathan Hurley 10 năm trước cách đây
mục cha
commit
03784ecad8
24 tập tin đã thay đổi với 1433 bổ sung83 xóa
  1. 258 0
      ambari-server/src/main/java/org/apache/ambari/server/api/query/JpaPredicateVisitor.java
  2. 50 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/AlertHistoryResourceDefinition.java
  3. 5 1
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
  4. 99 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertHistoryService.java
  5. 20 2
      ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
  6. 34 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/AlertHistoryRequest.java
  7. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
  8. 0 6
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
  9. 37 4
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProvider.java
  10. 28 8
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProvider.java
  11. 251 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertHistoryResourceProvider.java
  12. 26 11
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
  13. 11 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
  14. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
  15. 67 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertsDAO.java
  16. 50 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity_.java
  17. 94 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertHistoryEntity_.java
  18. 0 11
      ambari-server/src/main/resources/key_properties.json
  19. 0 29
      ambari-server/src/main/resources/properties.json
  20. 1 4
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java
  21. 1 3
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProviderTest.java
  22. 228 0
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertHistoryResourceProviderTest.java
  23. 1 3
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
  24. 168 0
      ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java

+ 258 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/query/JpaPredicateVisitor.java

@@ -0,0 +1,258 @@
+/**
+ * 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.api.query;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Path;
+import javax.persistence.criteria.Root;
+import javax.persistence.metamodel.SingularAttribute;
+
+import org.apache.ambari.server.controller.predicate.AlwaysPredicate;
+import org.apache.ambari.server.controller.predicate.ArrayPredicate;
+import org.apache.ambari.server.controller.predicate.CategoryPredicate;
+import org.apache.ambari.server.controller.predicate.ComparisonPredicate;
+import org.apache.ambari.server.controller.predicate.PredicateVisitor;
+import org.apache.ambari.server.controller.predicate.UnaryPredicate;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.utilities.PredicateHelper;
+
+/**
+ * The {@link JpaPredicateVisitor} is used to convert an Ambari
+ * {@link Predicate} into a JPA {@link javax.persistence.criteria.Predicate}.
+ */
+public abstract class JpaPredicateVisitor<T> implements PredicateVisitor {
+  /**
+   * JPA entity manager
+   */
+  private EntityManager m_entityManager;
+
+  /**
+   * Builds the {@link CriteriaQuery} from the {@link Predicate}.
+   */
+  private CriteriaBuilder m_builder;
+
+  /**
+   * The root that the {@code from} clause requests from.
+   */
+  private Root<T> m_root;
+
+  /**
+   * The query to submit to JPA.
+   */
+  private CriteriaQuery<T> m_query;
+
+  /**
+   * The last calculated predicate.
+   */
+  private javax.persistence.criteria.Predicate m_lastPredicate = null;
+
+  /**
+   * A queue of lists of {@link javax.persistence.criteria.Predicate}. Every
+   * time an {@code OR} or {@code AND} is encountered, a new chain (list) is
+   * created and the prior list is enqeued. When the logical statement is
+   * closed, the chain is completed and added to the prior chain's list.
+   */
+  private ArrayDeque<List<javax.persistence.criteria.Predicate>> m_queue =
+      new ArrayDeque<List<javax.persistence.criteria.Predicate>>();
+
+  /**
+   * Constructor.
+   *
+   * @param entityManager
+   *          the EM used to get a {@link CriteriaBuilder}.
+   * @param entityClass
+   *          the entity class being queried from.
+   */
+  public JpaPredicateVisitor(EntityManager entityManager, Class<T> entityClass) {
+    m_entityManager = entityManager;
+    m_builder = m_entityManager.getCriteriaBuilder();
+    m_query = m_builder.createQuery(entityClass);
+    m_root = m_query.from(entityClass);
+  }
+
+  /**
+   * Gets the entity class that is the root type in the JPA {@code from} clause.
+   *
+   * @return the entity class (not {@code null}).
+   */
+  public abstract Class<T> getEntityClass();
+
+  /**
+   * Gets the {@link SingularAttribute}s mapped to the specified Ambari-style
+   * property.
+   *
+   * @param propertyId
+   *          the Ambari-style property (not {@code null}).
+   * @return the {@link SingularAttribute}, or {@code null} if no mapping
+   *         exists.
+   */
+  public abstract List<? extends SingularAttribute<?, ?>> getPredicateMapping(
+      String propertyId);
+
+  /**
+   * Gets the final JPA {@link javax.persistence.criteria.Predicate} after the
+   * visitor is done traversing the Ambari {@link Predicate}.
+   *
+   * @return the predicate, or {@code null} if none.
+   */
+  public javax.persistence.criteria.Predicate getJpaPredicate() {
+    return m_lastPredicate;
+  }
+
+  /**
+   * Gets the query to use along with {@link #getJpaPredicate()}.
+   *
+   * @return the query (not {@code null}).
+   */
+  public CriteriaQuery<T> getCriteriaQuery() {
+    return m_query;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  public void acceptComparisonPredicate(ComparisonPredicate predicate) {
+    String propertyId = predicate.getPropertyId();
+
+    List<? extends SingularAttribute<?, ?>> singularAttributes = getPredicateMapping(propertyId);
+
+    if (null == singularAttributes || singularAttributes.size() == 0) {
+      return;
+    }
+
+    SingularAttribute<?, ?> lastSingularAttribute = null;
+    Path<Comparable> path = null;
+
+    for (SingularAttribute<?, ?> singularAttribute : singularAttributes) {
+      lastSingularAttribute = singularAttribute;
+
+      if (null == path) {
+        path = m_root.get(singularAttribute.getName());
+      } else {
+        path = path.get(singularAttribute.getName());
+      }
+    }
+
+    if (null == path) {
+      return;
+    }
+
+    String operator = predicate.getOperator();
+    Comparable<?> value = predicate.getValue();
+
+    // convert string to enum for proper JPA comparisons
+    Class<?> clazz = lastSingularAttribute.getJavaType();
+    if (clazz.isEnum()) {
+      Class<? extends Enum> enumClass = (Class<? extends Enum>) clazz;
+      value = Enum.valueOf(enumClass, value.toString());
+    }
+
+    javax.persistence.criteria.Predicate jpaPredicate = null;
+    if ("=".equals(operator)) {
+      jpaPredicate = m_builder.equal(path, value);
+    } else if ("<".equals(operator)) {
+      jpaPredicate = m_builder.lessThan(path, value);
+    } else if ("<=".equals(operator)) {
+      jpaPredicate = m_builder.lessThanOrEqualTo(path, value);
+    } else if (">".equals(operator)) {
+      jpaPredicate = m_builder.greaterThan(path, value);
+    } else if (">=".equals(operator)) {
+      jpaPredicate = m_builder.greaterThanOrEqualTo(path, value);
+    }
+
+    if (null == jpaPredicate) {
+      return;
+    }
+
+    if (null == m_queue.peekLast()) {
+      m_lastPredicate = jpaPredicate;
+    } else {
+      m_queue.peekLast().add(jpaPredicate);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void acceptArrayPredicate(ArrayPredicate predicate) {
+    // no predicates, no work
+    Predicate[] predicates = predicate.getPredicates();
+    if (predicates.length == 0) {
+      return;
+    }
+
+    // create a new list for all of the predicates in this chain
+    List<javax.persistence.criteria.Predicate> predicateList = new ArrayList<javax.persistence.criteria.Predicate>();
+    m_queue.add(predicateList);
+
+    // visit every child predicate so it can be added to the list
+    String operator = predicate.getOperator();
+    for (int i = 0; i < predicates.length; i++) {
+      PredicateHelper.visit(predicates[i], this);
+    }
+
+    // the list is done; deque and apply logical AND or OR
+    predicateList = m_queue.pollLast();
+
+    javax.persistence.criteria.Predicate jpaPredicate = null;
+    javax.persistence.criteria.Predicate[] array = new javax.persistence.criteria.Predicate[predicateList.size()];
+    array = predicateList.toArray(array);
+
+    if ("AND".equals(operator)) {
+      jpaPredicate = m_builder.and(array);
+    } else {
+      jpaPredicate = m_builder.or(array);
+    }
+
+    if (null == m_queue.peekLast()) {
+      m_lastPredicate = jpaPredicate;
+    } else {
+      m_queue.peekLast().add(jpaPredicate);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void acceptUnaryPredicate(UnaryPredicate predicate) {
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void acceptAlwaysPredicate(AlwaysPredicate predicate) {
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void acceptCategoryPredicate(CategoryPredicate predicate) {
+  }
+}

+ 50 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/AlertHistoryResourceDefinition.java

@@ -0,0 +1,50 @@
+/**
+ * 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.api.resources;
+
+import org.apache.ambari.server.controller.spi.Resource;
+
+/**
+ * Resource Definition for {@link Resource.Type#AlertHistory} types.
+ */
+public class AlertHistoryResourceDefinition extends BaseResourceDefinition {
+
+  /**
+   * Constructor.
+   *
+   */
+  public AlertHistoryResourceDefinition() {
+    super(Resource.Type.AlertHistory);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String getPluralName() {
+    return "alert_history";
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String getSingularName() {
+    return "alert_history";
+  }
+}

+ 5 - 1
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java

@@ -249,7 +249,7 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
       case Permission:
         resourceDefinition = new PermissionResourceDefinition();
         break;
-        
+
       case Alert:
         resourceDefinition = new AlertResourceDefinition();
         break;
@@ -258,6 +258,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new AlertDefResourceDefinition();
         break;
 
+      case AlertHistory:
+        resourceDefinition = new AlertHistoryResourceDefinition();
+        break;
+
       case AlertGroup:
         resourceDefinition = new AlertGroupResourceDefinition();
         break;

+ 99 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertHistoryService.java

@@ -0,0 +1,99 @@
+/**
+ * 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.api.services;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.controller.spi.Resource;
+
+/**
+ * Endpoint for alert history.
+ */
+public class AlertHistoryService extends BaseService {
+
+  private String clusterName = null;
+  private String serviceName = null;
+  private String hostName = null;
+
+  /**
+   * Constructor.
+   *
+   * @param clusterName
+   *          the cluster name (not {@code null}).
+   * @param serviceName
+   *          the service name, or {@code null} to return history across all
+   *          services.
+   * @param hostName
+   *          the host name, or {@code null} to return history across all hosts.
+   */
+  AlertHistoryService(String clusterName, String serviceName, String hostName) {
+    this.clusterName = clusterName;
+    this.serviceName = serviceName;
+    this.hostName = hostName;
+  }
+
+  @GET
+  @Produces("text/plain")
+  public Response getHistories(String body,
+      @Context HttpHeaders headers,
+      @Context UriInfo ui) {
+    return handleRequest(headers, body, ui, Request.Type.GET,
+        createResourceInstance(clusterName, null));
+  }
+
+  @GET
+  @Path("{alertHistoryId}")
+  @Produces("text/plain")
+  public Response getHistory(String body,
+      @Context HttpHeaders headers,
+      @Context UriInfo ui, @PathParam("alertHistoryId") Long id) {
+    return handleRequest(headers, body, ui, Request.Type.GET,
+        createResourceInstance(clusterName, id));
+  }
+
+  /**
+   * Create an alert history resource instance
+   *
+   * @param clusterName
+   * @return
+   */
+  private ResourceInstance createResourceInstance(String clusterName,
+      Long historyId) {
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+    mapIds.put(Resource.Type.Cluster, clusterName);
+    mapIds.put(Resource.Type.Service, serviceName);
+    mapIds.put(Resource.Type.Host, hostName);
+
+    if (null != historyId) {
+      mapIds.put(Resource.Type.AlertHistory, historyId.toString());
+    }
+
+    return createResource(Resource.Type.AlertHistory, mapIds);
+  }
+}

+ 20 - 2
ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java

@@ -357,7 +357,7 @@ public class ClusterService extends BaseService {
     hasPermission(Request.Type.valueOf(request.getMethod()), clusterName);
     return new ClusterPrivilegeService(clusterName);
   }
-  
+
   /**
    * Gets the alert definition service
    *
@@ -372,8 +372,26 @@ public class ClusterService extends BaseService {
 
     hasPermission(Request.Type.valueOf(request.getMethod()), clusterName);
     return new AlertService(clusterName, null, null);
-  }  
+  }
 
+  /**
+   * Gets the alert history service
+   *
+   * @param request
+   *          the request
+   * @param clusterName
+   *          the cluster name
+   *
+   * @return the alert history service
+   */
+  @Path("{clusterName}/alert_history")
+  public AlertHistoryService getAlertHistoryService(
+      @Context javax.ws.rs.core.Request request,
+      @PathParam("clusterName") String clusterName) {
+
+    hasPermission(Request.Type.valueOf(request.getMethod()), clusterName);
+    return new AlertHistoryService(clusterName, null, null);
+  }
 
   // ----- helper methods ----------------------------------------------------
 

+ 34 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/AlertHistoryRequest.java

@@ -0,0 +1,34 @@
+/**
+ * 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.controller;
+
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
+
+/**
+ * The {@link AlertHistoryRequest} encapsulates the data necessary to make a
+ * backend request for {@link AlertHistoryEntity}.
+ */
+public class AlertHistoryRequest {
+
+  /**
+   * An Ambari predicate.
+   */
+  public Predicate Predicate;
+
+}

+ 2 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java

@@ -47,6 +47,7 @@ import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.internal.AbstractControllerResourceProvider;
 import org.apache.ambari.server.controller.internal.AlertDefinitionResourceProvider;
 import org.apache.ambari.server.controller.internal.AlertGroupResourceProvider;
+import org.apache.ambari.server.controller.internal.AlertHistoryResourceProvider;
 import org.apache.ambari.server.controller.internal.AlertResourceProvider;
 import org.apache.ambari.server.controller.internal.AlertSummaryPropertyProvider;
 import org.apache.ambari.server.controller.internal.AlertTargetResourceProvider;
@@ -577,6 +578,7 @@ public class AmbariServer {
     ClusterResourceProvider.init(injector.getInstance(BlueprintDAO.class), ambariMetaInfo, injector.getInstance(ConfigHelper.class));
     AlertResourceProvider.init(injector);
     AlertDefinitionResourceProvider.init(injector);
+    AlertHistoryResourceProvider.init(injector);
     AlertGroupResourceProvider.init(injector);
     AlertSummaryPropertyProvider.init(injector);
     AlertTargetResourceProvider.init(injector);

+ 0 - 6
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java

@@ -151,12 +151,6 @@ public abstract class AbstractControllerResourceProvider extends AbstractResourc
         return new ValidationResourceProvider(propertyIds, keyPropertyIds, managementController);
       case Alert:
         return new AlertResourceProvider(propertyIds, keyPropertyIds, managementController);
-      case AlertDefinition:
-        return new AlertDefinitionResourceProvider(propertyIds, keyPropertyIds, managementController);
-      case AlertGroup:
-        return new AlertGroupResourceProvider(propertyIds, keyPropertyIds, managementController);
-      case AlertTarget:
-        return new AlertTargetResourceProvider(propertyIds, keyPropertyIds, managementController);
       case ClientConfig:
         return new ClientConfigResourceProvider(propertyIds, keyPropertyIds, managementController);
       default:

+ 37 - 4
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProvider.java

@@ -20,6 +20,7 @@ package org.apache.ambari.server.controller.internal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -104,6 +105,35 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
    */
   private static ActionQueue actionQueue;
 
+  /**
+   * The property ids for an alert defintion resource.
+   */
+  private static final Set<String> PROPERTY_IDS = new HashSet<String>();
+
+  /**
+   * The key property ids for an alert definition resource.
+   */
+  private static final Map<Resource.Type, String> KEY_PROPERTY_IDS = new HashMap<Resource.Type, String>();
+
+  static {
+    // properties
+    PROPERTY_IDS.add(ALERT_DEF_CLUSTER_NAME);
+    PROPERTY_IDS.add(ALERT_DEF_SERVICE_NAME);
+    PROPERTY_IDS.add(ALERT_DEF_COMPONENT_NAME);
+    PROPERTY_IDS.add(ALERT_DEF_ID);
+    PROPERTY_IDS.add(ALERT_DEF_NAME);
+    PROPERTY_IDS.add(ALERT_DEF_LABEL);
+    PROPERTY_IDS.add(ALERT_DEF_INTERVAL);
+    PROPERTY_IDS.add(ALERT_DEF_ENABLED);
+    PROPERTY_IDS.add(ALERT_DEF_SCOPE);
+    PROPERTY_IDS.add(ALERT_DEF_SOURCE);
+    PROPERTY_IDS.add(ALERT_DEF_ACTION_RUN_NOW);
+
+    // keys
+    KEY_PROPERTY_IDS.put(Resource.Type.AlertDefinition, ALERT_DEF_ID);
+    KEY_PROPERTY_IDS.put(Resource.Type.Cluster, ALERT_DEF_CLUSTER_NAME);
+  }
+
   /**
    * @param instance
    */
@@ -115,10 +145,13 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
     actionQueue = injector.getInstance(ActionQueue.class);
   }
 
-  AlertDefinitionResourceProvider(Set<String> propertyIds,
-      Map<Resource.Type, String> keyPropertyIds,
-      AmbariManagementController managementController) {
-    super(propertyIds, keyPropertyIds, managementController);
+  /**
+   * Constructor.
+   *
+   * @param controller
+   */
+  AlertDefinitionResourceProvider(AmbariManagementController controller) {
+    super(PROPERTY_IDS, KEY_PROPERTY_IDS, controller);
   }
 
   @Override

+ 28 - 8
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProvider.java

@@ -21,6 +21,7 @@ import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -35,7 +36,6 @@ import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.Request;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 import org.apache.ambari.server.controller.spi.Resource;
-import org.apache.ambari.server.controller.spi.Resource.Type;
 import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
 import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
@@ -71,6 +71,30 @@ public class AlertGroupResourceProvider extends
   private static final Set<String> PK_PROPERTY_IDS = new HashSet<String>(
       Arrays.asList(ALERT_GROUP_ID, ALERT_GROUP_CLUSTER_NAME));
 
+  /**
+   * The property ids for an alert group resource.
+   */
+  private static final Set<String> PROPERTY_IDS = new HashSet<String>();
+
+  /**
+   * The key property ids for an alert group resource.
+   */
+  private static final Map<Resource.Type, String> KEY_PROPERTY_IDS = new HashMap<Resource.Type, String>();
+
+  static {
+    // properties
+    PROPERTY_IDS.add(ALERT_GROUP_ID);
+    PROPERTY_IDS.add(ALERT_GROUP_CLUSTER_NAME);
+    PROPERTY_IDS.add(ALERT_GROUP_NAME);
+    PROPERTY_IDS.add(ALERT_GROUP_DEFAULT);
+    PROPERTY_IDS.add(ALERT_GROUP_DEFINITIONS);
+    PROPERTY_IDS.add(ALERT_GROUP_TARGETS);
+
+    // keys
+    KEY_PROPERTY_IDS.put(Resource.Type.AlertGroup, ALERT_GROUP_ID);
+    KEY_PROPERTY_IDS.put(Resource.Type.Cluster, ALERT_GROUP_CLUSTER_NAME);
+  }
+
   /**
    * Group/Target DAO
    */
@@ -97,14 +121,10 @@ public class AlertGroupResourceProvider extends
   /**
    * Constructor.
    *
-   * @param propertyIds
-   * @param keyPropertyIds
-   * @param managementController
+   * @param controller
    */
-  AlertGroupResourceProvider(Set<String> propertyIds,
-      Map<Type, String> keyPropertyIds,
-      AmbariManagementController managementController) {
-    super(propertyIds, keyPropertyIds, managementController);
+  AlertGroupResourceProvider(AmbariManagementController controller) {
+    super(PROPERTY_IDS, KEY_PROPERTY_IDS, controller);
   }
 
   @Override

+ 251 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertHistoryResourceProvider.java

@@ -0,0 +1,251 @@
+/**
+ * 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.controller.internal;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.AlertHistoryRequest;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.orm.dao.AlertsDAO;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
+import org.apache.ambari.server.state.Cluster;
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
+/**
+ * ResourceProvider for Alert History
+ */
+public class AlertHistoryResourceProvider extends
+    AbstractControllerResourceProvider {
+
+  public static final String ALERT_HISTORY = "AlertHistory";
+  public static final String ALERT_HISTORY_DEFINITION_ID = "AlertHistory/definition_id";
+  public static final String ALERT_HISTORY_DEFINITION_NAME = "AlertHistory/definition_name";
+  public static final String ALERT_HISTORY_ID = "AlertHistory/id";
+  public static final String ALERT_HISTORY_CLUSTER_NAME = "AlertHistory/cluster_name";
+  public static final String ALERT_HISTORY_SERVICE_NAME = "AlertHistory/service_name";
+  public static final String ALERT_HISTORY_COMPONENT_NAME = "AlertHistory/component_name";
+  public static final String ALERT_HISTORY_HOSTNAME = "AlertHistory/host_name";
+  public static final String ALERT_HISTORY_LABEL = "AlertHistory/label";
+  public static final String ALERT_HISTORY_STATE = "AlertHistory/state";
+  public static final String ALERT_HISTORY_TEXT = "AlertHistory/text";
+  public static final String ALERT_HISTORY_TIMESTAMP = "AlertHistory/timestamp";
+  public static final String ALERT_HISTORY_INSTANCE = "AlertHistory/instance";
+
+  private static final Set<String> PK_PROPERTY_IDS = new HashSet<String>(
+      Arrays.asList(ALERT_HISTORY_ID));
+
+  /**
+   * Used for querying alert history.
+   */
+  private static AlertsDAO s_dao = null;
+
+  /**
+   * The property ids for an alert history resource.
+   */
+  private static final Set<String> PROPERTY_IDS = new HashSet<String>();
+
+  /**
+   * The key property ids for an alert history resource.
+   */
+  private static final Map<Resource.Type, String> KEY_PROPERTY_IDS =
+      new HashMap<Resource.Type, String>();
+
+  static {
+    // properties
+    PROPERTY_IDS.add(ALERT_HISTORY_DEFINITION_ID);
+    PROPERTY_IDS.add(ALERT_HISTORY_DEFINITION_NAME);
+    PROPERTY_IDS.add(ALERT_HISTORY_ID);
+    PROPERTY_IDS.add(ALERT_HISTORY_CLUSTER_NAME);
+    PROPERTY_IDS.add(ALERT_HISTORY_SERVICE_NAME);
+    PROPERTY_IDS.add(ALERT_HISTORY_COMPONENT_NAME);
+    PROPERTY_IDS.add(ALERT_HISTORY_HOSTNAME);
+    PROPERTY_IDS.add(ALERT_HISTORY_LABEL);
+    PROPERTY_IDS.add(ALERT_HISTORY_STATE);
+    PROPERTY_IDS.add(ALERT_HISTORY_TEXT);
+    PROPERTY_IDS.add(ALERT_HISTORY_TIMESTAMP);
+    PROPERTY_IDS.add(ALERT_HISTORY_INSTANCE);
+
+    // keys
+    KEY_PROPERTY_IDS.put(Resource.Type.AlertHistory, ALERT_HISTORY_ID);
+    KEY_PROPERTY_IDS.put(Resource.Type.Cluster, ALERT_HISTORY_CLUSTER_NAME);
+  }
+
+  /**
+   * Static intializer for Guice.
+   *
+   * @param instance
+   */
+  @Inject
+  public static void init(Injector injector) {
+    s_dao = injector.getInstance(AlertsDAO.class);
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param controller
+   */
+  AlertHistoryResourceProvider(AmbariManagementController controller) {
+    super(PROPERTY_IDS, KEY_PROPERTY_IDS, controller);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return PK_PROPERTY_IDS;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public RequestStatus createResources(Request request) throws SystemException,
+      UnsupportedPropertyException, ResourceAlreadyExistsException,
+      NoSuchParentResourceException {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public RequestStatus updateResources(Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public RequestStatus deleteResources(Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+
+    Cluster cluster = null;
+    Set<String> requestPropertyIds = getRequestPropertyIds(request, predicate);
+    Set<Resource> results = new LinkedHashSet<Resource>();
+
+    Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate);
+    for (Map<String, Object> propertyMap : propertyMaps) {
+      String clusterName = (String) propertyMap.get(ALERT_HISTORY_CLUSTER_NAME);
+
+      if (null == clusterName || clusterName.isEmpty()) {
+        throw new IllegalArgumentException(
+            "Invalid argument, cluster name is required");
+      }
+
+      if (null == cluster) {
+        try {
+          cluster = getManagementController().getClusters().getCluster(
+              clusterName);
+        } catch (AmbariException e) {
+          throw new NoSuchResourceException(
+              "Parent Cluster resource doesn't exist", e);
+        }
+      }
+
+      // if asking for a single ID, get that historical entry and continue
+      // the for-loop
+      String id = (String) propertyMap.get(ALERT_HISTORY_ID);
+      if (null != id) {
+        AlertHistoryEntity entity = s_dao.findById(Long.parseLong(id));
+        if (null != entity) {
+          results.add(toResource(clusterName, entity, requestPropertyIds));
+          continue;
+        }
+      }
+
+      AlertHistoryRequest historyRequest = new AlertHistoryRequest();
+      historyRequest.Predicate = predicate;
+
+      List<AlertHistoryEntity> entities = s_dao.findAll(historyRequest);
+      for (AlertHistoryEntity entity : entities) {
+        results.add(toResource(cluster.getClusterName(), entity,
+            requestPropertyIds));
+      }
+    }
+
+    return results;
+  }
+
+  /**
+   * Converts the {@link AlertHistoryEntity} to a {@link Resource}.
+   *
+   * @param clusterName
+   *          the name of the cluster (not {@code null}).
+   * @param entity
+   *          the entity to convert (not {@code null}).
+   * @param requestedIds
+   *          the properties requested (not {@code null}).
+   * @return
+   */
+  private Resource toResource(String clusterName, AlertHistoryEntity entity,
+      Set<String> requestedIds) {
+    AlertDefinitionEntity definition = entity.getAlertDefinition();
+
+    Resource resource = new ResourceImpl(Resource.Type.AlertHistory);
+    resource.setProperty(ALERT_HISTORY_ID, entity.getAlertId());
+
+    setResourceProperty(resource, ALERT_HISTORY_CLUSTER_NAME, clusterName, requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_DEFINITION_ID, definition.getDefinitionId(), requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_DEFINITION_NAME, definition.getDefinitionName(), requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_SERVICE_NAME, entity.getServiceName(), requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_COMPONENT_NAME, entity.getComponentName(), requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_HOSTNAME, entity.getHostName(), requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_LABEL, entity.getAlertLabel(), requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_STATE, entity.getAlertState(), requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_TEXT, entity.getAlertText(), requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_TIMESTAMP, entity.getAlertTimestamp(), requestedIds);
+    setResourceProperty(resource, ALERT_HISTORY_INSTANCE, entity.getAlertInstance(), requestedIds);
+
+    return resource;
+  }
+}

+ 26 - 11
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java

@@ -28,14 +28,12 @@ import java.util.Map.Entry;
 import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.Request;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 import org.apache.ambari.server.controller.spi.Resource;
-import org.apache.ambari.server.controller.spi.Resource.Type;
 import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
 import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
@@ -55,7 +53,7 @@ import com.google.inject.Injector;
  * {@link AlertTargetEntity}.
  */
 public class AlertTargetResourceProvider extends
-    AbstractControllerResourceProvider {
+ AbstractResourceProvider {
 
   protected static final String ALERT_TARGET = "AlertTarget";
   protected static final String ALERT_TARGET_ID = "AlertTarget/id";
@@ -68,6 +66,29 @@ public class AlertTargetResourceProvider extends
   private static final Set<String> PK_PROPERTY_IDS = new HashSet<String>(
       Arrays.asList(ALERT_TARGET_ID, ALERT_TARGET_NAME));
 
+  /**
+   * The property ids for an alert target resource.
+   */
+  private static final Set<String> PROPERTY_IDS = new HashSet<String>();
+
+  /**
+   * The key property ids for an alert target resource.
+   */
+  private static final Map<Resource.Type, String> KEY_PROPERTY_IDS = new HashMap<Resource.Type, String>();
+
+  static {
+    // properties
+    PROPERTY_IDS.add(ALERT_TARGET_ID);
+    PROPERTY_IDS.add(ALERT_TARGET_NAME);
+    PROPERTY_IDS.add(ALERT_TARGET_DESCRIPTION);
+    PROPERTY_IDS.add(ALERT_TARGET_NOTIFICATION_TYPE);
+    PROPERTY_IDS.add(ALERT_TARGET_PROPERTIES);
+    PROPERTY_IDS.add(ALERT_TARGET_GROUPS);
+
+    // keys
+    KEY_PROPERTY_IDS.put(Resource.Type.AlertTarget, ALERT_TARGET_ID);
+  }
+
   /**
    * Target DAO
    */
@@ -93,15 +114,9 @@ public class AlertTargetResourceProvider extends
 
   /**
    * Constructor.
-   *
-   * @param propertyIds
-   * @param keyPropertyIds
-   * @param managementController
    */
-  AlertTargetResourceProvider(Set<String> propertyIds,
-      Map<Type, String> keyPropertyIds,
-      AmbariManagementController managementController) {
-    super(propertyIds, keyPropertyIds, managementController);
+  AlertTargetResourceProvider() {
+    super(PROPERTY_IDS, KEY_PROPERTY_IDS);
   }
 
   @Override

+ 11 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java

@@ -21,13 +21,14 @@ package org.apache.ambari.server.controller.internal;
 import java.util.Map;
 import java.util.Set;
 
-import com.google.inject.Inject;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.AmbariServer;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 
+import com.google.inject.Inject;
+
 /**
  * The default provider module implementation.
  */
@@ -83,6 +84,15 @@ public class DefaultProviderModule extends AbstractProviderModule {
         return new LdapSyncEventResourceProvider(managementController);
       case UserPrivilege:
         return new UserPrivilegeResourceProvider();
+      case AlertDefinition:
+        return new AlertDefinitionResourceProvider(managementController);
+      case AlertHistory:
+        return new AlertHistoryResourceProvider(managementController);
+      case AlertTarget:
+        return new AlertTargetResourceProvider();
+      case AlertGroup:
+        return new AlertGroupResourceProvider(managementController);
+
       default:
         return AbstractControllerResourceProvider.getResourceProvider(type, propertyIds,
             keyPropertyIds, managementController);

+ 2 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java

@@ -116,6 +116,7 @@ public interface Resource {
     Permission,
     Alert,
     AlertDefinition,
+    AlertHistory,
     AlertGroup,
     AlertTarget,
     AmbariPrivilege,
@@ -202,6 +203,7 @@ public interface Resource {
     public static final Type Permission = InternalType.Permission.getType();
     public static final Type Alert = InternalType.Alert.getType();
     public static final Type AlertDefinition = InternalType.AlertDefinition.getType();
+    public static final Type AlertHistory = InternalType.AlertHistory.getType();
     public static final Type AlertGroup = InternalType.AlertGroup.getType();
     public static final Type AlertTarget = InternalType.AlertTarget.getType();
     public static final Type AmbariPrivilege = InternalType.AmbariPrivilege.getType();

+ 67 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertsDAO.java

@@ -24,10 +24,17 @@ import java.util.List;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.metamodel.SingularAttribute;
 
+import org.apache.ambari.server.api.query.JpaPredicateVisitor;
+import org.apache.ambari.server.controller.AlertHistoryRequest;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.utilities.PredicateHelper;
 import org.apache.ambari.server.orm.RequiresSession;
 import org.apache.ambari.server.orm.entities.AlertCurrentEntity;
 import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
+import org.apache.ambari.server.orm.entities.AlertHistoryEntity_;
 import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.alert.Scope;
 import org.eclipse.persistence.config.HintValues;
@@ -184,6 +191,33 @@ public class AlertsDAO {
     return daoUtils.selectList(query);
   }
 
+  /**
+   * Finds all {@link AlertHistoryEntity} that match the provided
+   * {@link AlertHistoryRequest}. This method will make JPA do the heavy lifting
+   * of providing a slice of the result set.
+   *
+   * @param request
+   * @return
+   */
+  @Transactional
+  public List<AlertHistoryEntity> findAll(AlertHistoryRequest request) {
+    EntityManager entityManager = entityManagerProvider.get();
+
+    // convert the Ambari predicate into a JPA predicate
+    HistoryPredicateVisitor visitor = new HistoryPredicateVisitor();
+    PredicateHelper.visit(request.Predicate, visitor);
+
+    CriteriaQuery<AlertHistoryEntity> query = visitor.getCriteriaQuery();
+    javax.persistence.criteria.Predicate jpaPredicate = visitor.getJpaPredicate();
+
+    if (null != jpaPredicate) {
+      query.where(jpaPredicate);
+    }
+
+    TypedQuery<AlertHistoryEntity> typedQuery = entityManager.createQuery(query);
+    return daoUtils.selectList(typedQuery);
+  }
+
   /**
    * Gets the current alerts.
    *
@@ -537,4 +571,37 @@ public class AlertsDAO {
     query.setHint(QueryHints.REFRESH, HintValues.TRUE);
     return query;
   }
+
+  /**
+   * The {@link HistoryPredicateVisitor} is used to convert an Ambari
+   * {@link Predicate} into a JPA {@link javax.persistence.criteria.Predicate}.
+   */
+  private final class HistoryPredicateVisitor extends
+      JpaPredicateVisitor<AlertHistoryEntity> {
+
+    /**
+     * Constructor.
+     *
+     */
+    public HistoryPredicateVisitor() {
+      super(entityManagerProvider.get(), AlertHistoryEntity.class);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Class<AlertHistoryEntity> getEntityClass() {
+      return AlertHistoryEntity.class;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<? extends SingularAttribute<?, ?>> getPredicateMapping(
+        String propertyId) {
+      return AlertHistoryEntity_.getPredicateMapping().get(propertyId);
+    }
+  }
 }

+ 50 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity_.java

@@ -0,0 +1,50 @@
+/*
+ * 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 javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.metamodel.SetAttribute;
+import javax.persistence.metamodel.SingularAttribute;
+import javax.persistence.metamodel.StaticMetamodel;
+
+import org.apache.ambari.server.state.alert.Scope;
+import org.apache.ambari.server.state.alert.SourceType;
+
+
+/**
+ * The {@link AlertDefinitionEntity_} is a strongly typed metamodel for creating
+ * {@link CriteriaQuery} for {@link AlertDefinitionEntity}.
+ */
+@StaticMetamodel(AlertDefinitionEntity.class)
+public class AlertDefinitionEntity_ {
+  public static volatile SingularAttribute<AlertDefinitionEntity, Long> definitionId;
+  public static volatile SingularAttribute<AlertDefinitionEntity, String> source;
+  public static volatile SingularAttribute<AlertDefinitionEntity, Long> clusterId;
+  public static volatile SingularAttribute<AlertDefinitionEntity, ClusterEntity> clusterEntity;
+  public static volatile SingularAttribute<AlertDefinitionEntity, String> componentName;
+  public static volatile SingularAttribute<AlertDefinitionEntity, String> definitionName;
+  public static volatile SingularAttribute<AlertDefinitionEntity, String> label;
+  public static volatile SingularAttribute<AlertDefinitionEntity, Scope> scope;
+  public static volatile SingularAttribute<AlertDefinitionEntity, Integer> enabled;
+  public static volatile SingularAttribute<AlertDefinitionEntity, String> hash;
+  public static volatile SingularAttribute<AlertDefinitionEntity, Integer> scheduleInterval;
+  public static volatile SingularAttribute<AlertDefinitionEntity, String> serviceName;
+  public static volatile SingularAttribute<AlertDefinitionEntity, SourceType> sourceType;
+  public static volatile SetAttribute<AlertDefinitionEntity, AlertGroupEntity> alertGroups;
+}

+ 94 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertHistoryEntity_.java

@@ -0,0 +1,94 @@
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.metamodel.SingularAttribute;
+import javax.persistence.metamodel.StaticMetamodel;
+
+import org.apache.ambari.server.controller.internal.AlertHistoryResourceProvider;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.state.AlertState;
+
+
+/**
+ * The {@link AlertHistoryEntity_} is a strongly typed metamodel for creating
+ * {@link CriteriaQuery} for {@link AlertHistoryEntity}.
+ */
+@StaticMetamodel(AlertHistoryEntity.class)
+public class AlertHistoryEntity_ {
+  public static volatile SingularAttribute<AlertHistoryEntity, Long> alertId;
+  public static volatile SingularAttribute<AlertHistoryEntity, String> alertInstance;
+  public static volatile SingularAttribute<AlertHistoryEntity, String> alertLabel;
+  public static volatile SingularAttribute<AlertHistoryEntity, AlertState> alertState;
+  public static volatile SingularAttribute<AlertHistoryEntity, String> alertText;
+  public static volatile SingularAttribute<AlertHistoryEntity, Long> alertTimestamp;
+  public static volatile SingularAttribute<AlertHistoryEntity, Long> clusterId;
+  public static volatile SingularAttribute<AlertHistoryEntity, String> componentName;
+  public static volatile SingularAttribute<AlertHistoryEntity, String> hostName;
+  public static volatile SingularAttribute<AlertHistoryEntity, String> serviceName;
+  public static volatile SingularAttribute<AlertHistoryEntity, AlertDefinitionEntity> alertDefinition;
+
+  /**
+   * Gets a mapping of between a resource provider property, like
+   * {@link AlertHistoryResourceProvider#ALERT_HISTORY_SERVICE_NAME} to a
+   * metamodel {@link SingularAttribute}.
+   * <p/>
+   * This is used when converting an Ambari {@link Predicate} into a JPA
+   * {@link javax.persistence.criteria.Predicate} and we need a type-safe
+   * conversion between "category/property" and JPA field names.
+   * <p/>
+   * Multiple {@link SingularAttribute} instances can be chained together in
+   * order to provide an {@code entity.subEntity.field} reference.
+   *
+   * @return
+   */
+  @SuppressWarnings("unchecked")
+  public static Map<String, List<? extends SingularAttribute<?, ?>>> getPredicateMapping() {
+    Map<String, List<? extends SingularAttribute<?, ?>>> mapping = new HashMap<String, List<? extends SingularAttribute<?, ?>>>();
+
+    mapping.put(AlertHistoryResourceProvider.ALERT_HISTORY_SERVICE_NAME,
+        Collections.singletonList(serviceName));
+
+    mapping.put(
+        AlertHistoryResourceProvider.ALERT_HISTORY_COMPONENT_NAME,
+        Collections.singletonList(componentName));
+
+    mapping.put(AlertHistoryResourceProvider.ALERT_HISTORY_STATE,
+        Collections.singletonList(alertState));
+
+    mapping.put(AlertHistoryResourceProvider.ALERT_HISTORY_TIMESTAMP,
+        Collections.singletonList(alertTimestamp));
+
+    mapping.put(AlertHistoryResourceProvider.ALERT_HISTORY_HOSTNAME,
+        Collections.singletonList(hostName));
+
+    // AlertHistory.alertDefinition.definitionName = foo
+    mapping.put(AlertHistoryResourceProvider.ALERT_HISTORY_DEFINITION_NAME,
+        Arrays.asList(alertDefinition, AlertDefinitionEntity_.definitionName));
+
+    return mapping;
+  }
+}

+ 0 - 11
ambari-server/src/main/resources/key_properties.json

@@ -151,17 +151,6 @@
     "Host": "Alert/host_name",
     "Alert": "Alert/id"
   },
-  "AlertDefinition": {
-    "Cluster": "AlertDefinition/cluster_name",
-    "AlertDefinition": "AlertDefinition/id"
-  },
-  "AlertTarget": {
-    "AlertTarget": "AlertTarget/id"
-  },
-  "AlertGroup": {
-    "Cluster": "AlertGroup/cluster_name",
-    "AlertGroup": "AlertGroup/id"
-  },  
   "ClientConfig": {
     "Cluster": "ServiceComponentInfo/cluster_name",
     "Service": "ServiceComponentInfo/service_name",

+ 0 - 29
ambari-server/src/main/resources/properties.json

@@ -437,35 +437,6 @@
       "Alert/service_name",
       "Alert/scope"
     ],
-    "AlertDefinition": [
-      "AlertDefinition/cluster_name",
-      "AlertDefinition/service_name",
-      "AlertDefinition/component_name",
-      "AlertDefinition/id",
-      "AlertDefinition/name",
-      "AlertDefinition/label",
-      "AlertDefinition/interval",
-      "AlertDefinition/enabled",
-      "AlertDefinition/scope",
-      "AlertDefinition/source",
-      "AlertDefinition/run_now"
-    ],
-    "AlertGroup": [
-      "AlertGroup/id",
-      "AlertGroup/name",
-      "AlertGroup/cluster_name",
-      "AlertGroup/default",
-      "AlertGroup/definitions",
-      "AlertGroup/targets"
-    ],
-    "AlertTarget": [
-      "AlertTarget/id",
-      "AlertTarget/name",
-      "AlertTarget/description",      
-      "AlertTarget/notification_type",
-      "AlertTarget/properties",
-      "AlertTarget/groups"
-    ],
     "ClientConfig":[
         "ServiceComponentInfo/service_name",
         "ServiceComponentInfo/component_name",

+ 1 - 4
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java

@@ -451,10 +451,7 @@ public class AlertDefinitionResourceProviderTest {
    * @return
    */
   private AlertDefinitionResourceProvider createProvider(AmbariManagementController amc) {
-    return new AlertDefinitionResourceProvider(
-        PropertyHelper.getPropertyIds(Resource.Type.AlertDefinition),
-        PropertyHelper.getKeyPropertyIds(Resource.Type.AlertDefinition),
-        amc);
+    return new AlertDefinitionResourceProvider(amc);
   }
 
   /**

+ 1 - 3
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProviderTest.java

@@ -528,9 +528,7 @@ public class AlertGroupResourceProviderTest {
   private AlertGroupResourceProvider createProvider(
       AmbariManagementController amc) {
 
-    return new AlertGroupResourceProvider(
-        PropertyHelper.getPropertyIds(Resource.Type.AlertGroup),
-        PropertyHelper.getKeyPropertyIds(Resource.Type.AlertGroup), amc);
+    return new AlertGroupResourceProvider(amc);
   }
 
   /**

+ 228 - 0
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertHistoryResourceProviderTest.java

@@ -0,0 +1,228 @@
+/**
+ * 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.controller.internal;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createStrictMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ambari.server.controller.AlertHistoryRequest;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PredicateBuilder;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.metadata.ActionMetadata;
+import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
+import org.apache.ambari.server.orm.dao.AlertsDAO;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
+import org.apache.ambari.server.state.AlertState;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.inject.Binder;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import com.google.inject.util.Modules;
+
+/**
+ * {@link AlertHistoryResourceProvider} tests.
+ */
+public class AlertHistoryResourceProviderTest {
+
+  private AlertsDAO m_dao = null;
+  private Injector m_injector;
+
+  @Before
+  public void before() {
+    m_dao = createStrictMock(AlertsDAO.class);
+
+    m_injector = Guice.createInjector(Modules.override(
+        new InMemoryDefaultTestModule()).with(new MockModule()));
+
+    AlertHistoryResourceProvider.init(m_injector);
+  }
+
+  /**
+   * @throws Exception
+   */
+  @Test
+  public void testGetResourcesNoPredicate() throws Exception {
+    AlertHistoryResourceProvider provider = createProvider(null);
+
+    Request request = PropertyHelper.getReadRequest(
+        "AlertHistory/cluster_name", "AlertHistory/id");
+
+    Set<Resource> results = provider.getResources(request, null);
+    assertEquals(0, results.size());
+  }
+
+  /**
+   * @throws Exception
+   */
+  @Test
+  public void testGetResourcesClusterPredicate() throws Exception {
+    Request request = PropertyHelper.getReadRequest(
+        AlertHistoryResourceProvider.ALERT_HISTORY_CLUSTER_NAME,
+        AlertHistoryResourceProvider.ALERT_HISTORY_DEFINITION_ID,
+        AlertHistoryResourceProvider.ALERT_HISTORY_DEFINITION_NAME,
+        AlertHistoryResourceProvider.ALERT_HISTORY_COMPONENT_NAME,
+        AlertHistoryResourceProvider.ALERT_HISTORY_HOSTNAME,
+        AlertHistoryResourceProvider.ALERT_HISTORY_STATE);
+
+    AmbariManagementController amc = createMock(AmbariManagementController.class);
+    Clusters clusters = createMock(Clusters.class);
+    Cluster cluster = createMock(Cluster.class);
+    expect(amc.getClusters()).andReturn(clusters).atLeastOnce();
+    expect(clusters.getCluster((String) anyObject())).andReturn(cluster).atLeastOnce();
+    expect(cluster.getClusterId()).andReturn(Long.valueOf(1)).anyTimes();
+    expect(cluster.getClusterName()).andReturn("c1").atLeastOnce();
+
+    Predicate predicate = new PredicateBuilder().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_CLUSTER_NAME).equals("c1").toPredicate();
+
+    expect(m_dao.findAll(EasyMock.anyObject(AlertHistoryRequest.class))).andReturn(
+        getMockEntities());
+
+    replay(amc, clusters, cluster, m_dao);
+
+    AlertHistoryResourceProvider provider = createProvider(amc);
+    Set<Resource> results = provider.getResources(request, predicate);
+
+    assertEquals(1, results.size());
+
+    Resource r = results.iterator().next();
+
+    Assert.assertEquals(
+        "namenode_definition",
+        r.getPropertyValue(AlertHistoryResourceProvider.ALERT_HISTORY_DEFINITION_NAME));
+
+    Assert.assertEquals(AlertState.WARNING,
+        r.getPropertyValue(AlertHistoryResourceProvider.ALERT_HISTORY_STATE));
+
+    verify(amc, clusters, cluster, m_dao);
+  }
+
+  /**
+   * @throws Exception
+   */
+  @Test
+  public void testGetSingleResource() throws Exception {
+    Request request = PropertyHelper.getReadRequest(
+        AlertHistoryResourceProvider.ALERT_HISTORY_CLUSTER_NAME,
+        AlertHistoryResourceProvider.ALERT_HISTORY_DEFINITION_ID,
+        AlertHistoryResourceProvider.ALERT_HISTORY_DEFINITION_NAME,
+        AlertHistoryResourceProvider.ALERT_HISTORY_COMPONENT_NAME,
+        AlertHistoryResourceProvider.ALERT_HISTORY_HOSTNAME,
+        AlertHistoryResourceProvider.ALERT_HISTORY_STATE);
+
+    AmbariManagementController amc = createMock(AmbariManagementController.class);
+    Clusters clusters = createMock(Clusters.class);
+    Cluster cluster = createMock(Cluster.class);
+    expect(amc.getClusters()).andReturn(clusters).atLeastOnce();
+    expect(clusters.getCluster((String) anyObject())).andReturn(cluster).atLeastOnce();
+    expect(cluster.getClusterId()).andReturn(Long.valueOf(1)).anyTimes();
+
+    Predicate predicate = new PredicateBuilder().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_CLUSTER_NAME).equals("c1").and().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_ID).equals("1").toPredicate();
+
+    expect(m_dao.findById(1L)).andReturn(getMockEntities().get(0));
+
+    replay(amc, clusters, cluster, m_dao);
+
+    AlertHistoryResourceProvider provider = createProvider(amc);
+    Set<Resource> results = provider.getResources(request, predicate);
+
+    assertEquals(1, results.size());
+
+    Resource r = results.iterator().next();
+
+    Assert.assertEquals(
+        "namenode_definition",
+        r.getPropertyValue(AlertHistoryResourceProvider.ALERT_HISTORY_DEFINITION_NAME));
+
+    Assert.assertEquals(AlertState.WARNING,
+        r.getPropertyValue(AlertHistoryResourceProvider.ALERT_HISTORY_STATE));
+  }
+
+  /**
+   * @param amc
+   * @return
+   */
+  private AlertHistoryResourceProvider createProvider(
+      AmbariManagementController amc) {
+    return new AlertHistoryResourceProvider(amc);
+  }
+
+  /**
+   * @return
+   */
+  private List<AlertHistoryEntity> getMockEntities() throws Exception {
+    AlertDefinitionEntity definition = new AlertDefinitionEntity();
+    definition.setClusterId(1L);
+    definition.setComponentName("NAMENODE");
+    definition.setDefinitionName("namenode_definition");
+    definition.setEnabled(true);
+    definition.setServiceName("HDFS");
+
+    AlertHistoryEntity entity = new AlertHistoryEntity();
+    entity.setAlertId(1L);
+    entity.setAlertDefinition(definition);
+    entity.setClusterId(Long.valueOf(1L));
+    entity.setComponentName(null);
+    entity.setAlertText("Mock Label");
+    entity.setServiceName("HDFS");
+    entity.setAlertState(AlertState.WARNING);
+    entity.setAlertTimestamp(System.currentTimeMillis());
+    return Arrays.asList(entity);
+  }
+
+  /**
+  *
+  */
+  private class MockModule implements Module {
+    /**
+    *
+    */
+    @Override
+    public void configure(Binder binder) {
+      binder.bind(AlertsDAO.class).toInstance(m_dao);
+      binder.bind(Clusters.class).toInstance(
+          EasyMock.createNiceMock(Clusters.class));
+      binder.bind(Cluster.class).toInstance(
+          EasyMock.createNiceMock(Cluster.class));
+      binder.bind(ActionMetadata.class);
+    }
+  }
+}

+ 1 - 3
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java

@@ -317,9 +317,7 @@ public class AlertTargetResourceProviderTest {
    */
   private AlertTargetResourceProvider createProvider(
       AmbariManagementController amc) {
-    return new AlertTargetResourceProvider(
-        PropertyHelper.getPropertyIds(Resource.Type.AlertTarget),
-        PropertyHelper.getKeyPropertyIds(Resource.Type.AlertTarget), amc);
+    return new AlertTargetResourceProvider();
   }
 
   /**

+ 168 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertsDAOTest.java

@@ -37,6 +37,10 @@ import java.util.UUID;
 
 import junit.framework.Assert;
 
+import org.apache.ambari.server.controller.AlertHistoryRequest;
+import org.apache.ambari.server.controller.internal.AlertHistoryResourceProvider;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.events.listeners.AlertMaintenanceModeListener;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
@@ -763,7 +767,171 @@ public class AlertsDAOTest {
         assertEquals(MaintenanceState.OFF, current.getMaintenanceState());
       }
     }
+  }
+
+  /**
+   * Tests that the Ambari {@link Predicate} can be converted and submitted to
+   * JPA correctly to return a restricted result set.
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testAlertHistoryPredicate() throws Exception {
+    Cluster cluster = initializeNewCluster();
+
+    // remove any definitions and start over
+    List<AlertDefinitionEntity> definitions = m_definitionDao.findAll();
+    for (AlertDefinitionEntity definition : definitions) {
+      m_definitionDao.remove(definition);
+    }
+
+    // create some definitions
+    AlertDefinitionEntity namenode = new AlertDefinitionEntity();
+    namenode.setDefinitionName("NAMENODE");
+    namenode.setServiceName("HDFS");
+    namenode.setComponentName("NAMENODE");
+    namenode.setClusterId(cluster.getClusterId());
+    namenode.setHash(UUID.randomUUID().toString());
+    namenode.setScheduleInterval(Integer.valueOf(60));
+    namenode.setScope(Scope.ANY);
+    namenode.setSource("{\"type\" : \"SCRIPT\"}");
+    namenode.setSourceType(SourceType.SCRIPT);
+    m_definitionDao.create(namenode);
+
+    AlertDefinitionEntity datanode = new AlertDefinitionEntity();
+    datanode.setDefinitionName("DATANODE");
+    datanode.setServiceName("HDFS");
+    datanode.setComponentName("DATANODE");
+    datanode.setClusterId(cluster.getClusterId());
+    datanode.setHash(UUID.randomUUID().toString());
+    datanode.setScheduleInterval(Integer.valueOf(60));
+    datanode.setScope(Scope.HOST);
+    datanode.setSource("{\"type\" : \"SCRIPT\"}");
+    datanode.setSourceType(SourceType.SCRIPT);
+    m_definitionDao.create(datanode);
+
+    AlertDefinitionEntity aggregate = new AlertDefinitionEntity();
+    aggregate.setDefinitionName("YARN_AGGREGATE");
+    aggregate.setServiceName("YARN");
+    aggregate.setComponentName(null);
+    aggregate.setClusterId(cluster.getClusterId());
+    aggregate.setHash(UUID.randomUUID().toString());
+    aggregate.setScheduleInterval(Integer.valueOf(60));
+    aggregate.setScope(Scope.SERVICE);
+    aggregate.setSource("{\"type\" : \"SCRIPT\"}");
+    aggregate.setSourceType(SourceType.SCRIPT);
+    m_definitionDao.create(aggregate);
+
+    // create some history
+    AlertHistoryEntity nnHistory = new AlertHistoryEntity();
+    nnHistory.setAlertState(AlertState.OK);
+    nnHistory.setServiceName(namenode.getServiceName());
+    nnHistory.setComponentName(namenode.getComponentName());
+    nnHistory.setClusterId(cluster.getClusterId());
+    nnHistory.setAlertDefinition(namenode);
+    nnHistory.setAlertLabel(namenode.getDefinitionName());
+    nnHistory.setAlertText(namenode.getDefinitionName());
+    nnHistory.setAlertTimestamp(calendar.getTimeInMillis());
+    nnHistory.setHostName(HOSTNAME);
+    m_dao.create(nnHistory);
+
+    AlertHistoryEntity dnHistory = new AlertHistoryEntity();
+    dnHistory.setAlertState(AlertState.WARNING);
+    dnHistory.setServiceName(datanode.getServiceName());
+    dnHistory.setComponentName(datanode.getComponentName());
+    dnHistory.setClusterId(cluster.getClusterId());
+    dnHistory.setAlertDefinition(datanode);
+    dnHistory.setAlertLabel(datanode.getDefinitionName());
+    dnHistory.setAlertText(datanode.getDefinitionName());
+    dnHistory.setAlertTimestamp(calendar.getTimeInMillis());
+    dnHistory.setHostName(HOSTNAME);
+    m_dao.create(dnHistory);
+
+    AlertHistoryEntity aggregateHistory = new AlertHistoryEntity();
+    aggregateHistory.setAlertState(AlertState.CRITICAL);
+    aggregateHistory.setServiceName(aggregate.getServiceName());
+    aggregateHistory.setComponentName(aggregate.getComponentName());
+    aggregateHistory.setClusterId(cluster.getClusterId());
+    aggregateHistory.setAlertDefinition(aggregate);
+    aggregateHistory.setAlertLabel(aggregate.getDefinitionName());
+    aggregateHistory.setAlertText(aggregate.getDefinitionName());
+    aggregateHistory.setAlertTimestamp(calendar.getTimeInMillis());
+    m_dao.create(aggregateHistory);
+
+    List<AlertHistoryEntity> histories = m_dao.findAll();
+    assertEquals(3, histories.size());
+
+    Predicate clusterPredicate = null;
+    Predicate hdfsPredicate = null;
+    Predicate yarnPredicate = null;
+    Predicate clusterAndHdfsPredicate = null;
+    Predicate clusterAndHdfsAndCriticalPredicate = null;
+    Predicate hdfsAndCriticalOrWarningPredicate = null;
+    Predicate alertNamePredicate = null;
+
+    clusterPredicate = new PredicateBuilder().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_CLUSTER_NAME).equals("c1").toPredicate();
+
+    AlertHistoryRequest request = new AlertHistoryRequest();
+
+    request.Predicate = clusterPredicate;
+    histories = m_dao.findAll(request);
+    assertEquals(3, histories.size());
+
+    hdfsPredicate = new PredicateBuilder().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_SERVICE_NAME).equals("HDFS").toPredicate();
+
+    yarnPredicate = new PredicateBuilder().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_SERVICE_NAME).equals("YARN").toPredicate();
+
+    clusterAndHdfsPredicate = new PredicateBuilder().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_CLUSTER_NAME).equals("c1").and().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_SERVICE_NAME).equals("HDFS").toPredicate();
+
+    clusterAndHdfsPredicate = new PredicateBuilder().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_CLUSTER_NAME).equals("c1").and().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_SERVICE_NAME).equals("HDFS").toPredicate();
+
+    clusterAndHdfsAndCriticalPredicate = new PredicateBuilder().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_CLUSTER_NAME).equals("c1").and().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_SERVICE_NAME).equals("HDFS").and().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_STATE).equals(
+        AlertState.CRITICAL.name()).toPredicate();
+
+    hdfsAndCriticalOrWarningPredicate = new PredicateBuilder().begin().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_SERVICE_NAME).equals("HDFS").and().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_STATE).equals(
+        AlertState.CRITICAL.name()).end().or().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_STATE).equals(
+        AlertState.WARNING.name()).toPredicate();
+
+    alertNamePredicate = new PredicateBuilder().property(
+        AlertHistoryResourceProvider.ALERT_HISTORY_DEFINITION_NAME).equals(
+        "NAMENODE").toPredicate();
+
+    request.Predicate = hdfsPredicate;
+    histories = m_dao.findAll(request);
+    assertEquals(2, histories.size());
+
+    request.Predicate = yarnPredicate;
+    histories = m_dao.findAll(request);
+    assertEquals(1, histories.size());
+
+    request.Predicate = clusterAndHdfsPredicate;
+    histories = m_dao.findAll(request);
+    assertEquals(2, histories.size());
+
+    request.Predicate = clusterAndHdfsAndCriticalPredicate;
+    histories = m_dao.findAll(request);
+    assertEquals(0, histories.size());
+
+    request.Predicate = hdfsAndCriticalOrWarningPredicate;
+    histories = m_dao.findAll(request);
+    assertEquals(1, histories.size());
 
+    request.Predicate = alertNamePredicate;
+    histories = m_dao.findAll(request);
+    assertEquals(1, histories.size());
   }
 
   private Cluster initializeNewCluster() throws Exception {