Repository: ambari
Updated Branches:
  refs/heads/branch-alerts-dev ae8f1e77e -> 76fc94ec1


AMBARI-7051 - Alerts: Expose Creation and Updating of Group Targets & 
Definitions (jonathanhurley)


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

Branch: refs/heads/branch-alerts-dev
Commit: 76fc94ec1ab6ee904e0efa254c19521a201c06d4
Parents: ae8f1e7
Author: Jonathan Hurley <jhur...@hortonworks.com>
Authored: Wed Aug 27 21:07:43 2014 -0400
Committer: Jonathan Hurley <jhur...@hortonworks.com>
Committed: Thu Aug 28 09:38:28 2014 -0400

----------------------------------------------------------------------
 .../python/ambari_agent/alerts/base_alert.py    |  45 ++-
 .../python/ambari_agent/alerts/port_alert.py    |  42 +--
 .../server/api/services/AlertGroupService.java  |  10 +
 .../controller/AlertDefinitionResponse.java     | 143 +++++++++
 .../internal/AlertGroupResourceProvider.java    | 144 ++++++++-
 .../server/orm/dao/AlertDefinitionDAO.java      |  16 +
 .../ambari/server/orm/dao/AlertDispatchDAO.java |  18 +-
 .../orm/entities/AlertDefinitionEntity.java     |  37 ++-
 .../server/orm/entities/AlertGroupEntity.java   |  56 +++-
 .../server/orm/entities/AlertTargetEntity.java  |   3 +-
 .../ambari/server/state/alert/AlertTarget.java  |  37 ++-
 .../AlertGroupResourceProviderTest.java         | 295 +++++++++++++++----
 .../AlertTargetResourceProviderTest.java        |   2 +-
 .../server/orm/dao/AlertDefinitionDAOTest.java  |  18 +-
 .../server/orm/dao/AlertDispatchDAOTest.java    |  53 ++--
 15 files changed, 773 insertions(+), 146 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py 
b/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
index 88c7f92..bf8ada6 100644
--- a/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
+++ b/ambari-agent/src/main/python/ambari_agent/alerts/base_alert.py
@@ -34,7 +34,7 @@ class BaseAlert(object):
     self.alert_meta = alert_meta
     self.alert_source_meta = alert_source_meta
     self.cluster = ''
-    self.hostName = ''
+    self.host_name = ''
     self._lookup_keys = []
     
     
@@ -54,7 +54,7 @@ class BaseAlert(object):
   def set_cluster(self, cluster, host):
     ''' sets cluster information for the alert '''
     self.cluster = cluster
-    self.hostName = host
+    self.host_name = host
   
   def collect(self):
     ''' method used for collection.  defers to _collect() '''
@@ -123,4 +123,43 @@ class BaseAlert(object):
     res[0] = the result code
     res[1] = the list of arguments supplied to the reporting text for the 
result code
     '''  
-    raise NotImplementedError
\ No newline at end of file
+    raise NotImplementedError
+
+  '''
+  See RFC3986, Appendix B
+  Tested on the following cases:
+    "192.168.54.1"
+    "192.168.54.2:7661
+    "hdfs://192.168.54.3/foo/bar"
+    "ftp://192.168.54.4:7842/foo/bar";
+
+    Returns None if only a port is passsed in
+  '''
+  @staticmethod
+  def get_host_from_url(uri):
+    # RFC3986, Appendix B
+    parts = 
re.findall('^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?', uri)
+
+    # index of parts
+    # scheme    = 1
+    # authority = 3
+    # path      = 4
+    # query     = 6
+    # fragment  = 8
+
+    host_and_port = uri
+    if 0 == len(parts[0][1]):
+      host_and_port = parts[0][4]
+    elif 0 == len(parts[0][2]):
+      host_and_port = parts[0][1]
+    elif parts[0][2].startswith("//"):
+      host_and_port = parts[0][3]
+
+    if -1 == host_and_port.find(':'):
+      # if no : then it might only be a port; if it's a port, return this host
+      if host_and_port.isdigit():
+        return None
+
+      return host_and_port
+    else:
+      return host_and_port.split(':')[0]

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py 
b/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py
index eaf744a..1d34410 100644
--- a/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py
+++ b/ambari-agent/src/main/python/ambari_agent/alerts/port_alert.py
@@ -39,9 +39,11 @@ class PortAlert(BaseAlert):
   def _collect(self):
     urivalue = self._lookup_property_value(self.uri)
 
-    host = get_host_from_url(self, urivalue)
     port = self.port
-    
+    host = BaseAlert.get_host_from_url(urivalue)
+    if host is None:
+      host = self.host_name
+
     try:
       port = int(get_port_from_url(urivalue))
     except:
@@ -67,39 +69,3 @@ class PortAlert(BaseAlert):
         except:
           pass
 
-'''
-See RFC3986, Appendix B
-Tested on the following cases:
-  "192.168.54.1"
-  "192.168.54.2:7661
-  "hdfs://192.168.54.3/foo/bar"
-  "ftp://192.168.54.4:7842/foo/bar";
-'''    
-def get_host_from_url(self, uri):
-  # RFC3986, Appendix B
-  parts = 
re.findall('^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?', uri)
-
-  # index of parts
-  # scheme    = 1
-  # authority = 3
-  # path      = 4
-  # query     = 6
-  # fragment  = 8
- 
-  host_and_port = uri
-  if 0 == len(parts[0][1]):
-    host_and_port = parts[0][4]
-  elif 0 == len(parts[0][2]):
-    host_and_port = parts[0][1]
-  elif parts[0][2].startswith("//"):
-    host_and_port = parts[0][3]
-
-  if -1 == host_and_port.find(':'):
-    # if no : then it might only be a port; if it's a port, return this host
-    if host_and_port.isdigit():
-      return self.hostName
-
-    return host_and_port
-  else:
-    return host_and_port.split(':')[0]
-

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertGroupService.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertGroupService.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertGroupService.java
index c2e5048..12cdb70 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertGroupService.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertGroupService.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -80,6 +81,15 @@ public class AlertGroupService extends BaseService {
         createAlertGroupResource(m_clusterName, null));
   }
 
+  @PUT
+  @Produces("text/plain")
+  @Path("{groupId}")
+  public Response updateGroup(String body, @Context HttpHeaders headers,
+      @Context UriInfo ui, @PathParam("groupId") Long groupId) {
+    return handleRequest(headers, body, ui, Request.Type.PUT,
+        createAlertGroupResource(m_clusterName, groupId));
+  }
+
   @DELETE
   @Produces("text/plain")
   @Path("{groupId}")

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/main/java/org/apache/ambari/server/controller/AlertDefinitionResponse.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/AlertDefinitionResponse.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/AlertDefinitionResponse.java
new file mode 100644
index 0000000..26e2f24
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/AlertDefinitionResponse.java
@@ -0,0 +1,143 @@
+/**
+® * 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.orm.entities.AlertDefinitionEntity;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+
+/**
+ * The {@link AlertDefinitionResponse} encapsulates the definition information
+ * that should be serialized and returned in REST requests for alerts, groups,
+ * and targets.
+ */
+public class AlertDefinitionResponse {
+
+  private String serviceName = null;
+  private String componentName = null;
+  private String name = null;
+  private String label = null;
+  private Long definitionId;
+
+  /**
+   * @return the definitionId
+   */
+  @JsonProperty("id")
+  public Long getDefinitionId() {
+    return definitionId;
+  }
+
+  /**
+   * @param definitionId
+   *          the definitionId to set
+   */
+  public void setDefinitionId(Long definitionId) {
+    this.definitionId = definitionId;
+  }
+
+  /**
+   * @return the service name
+   */
+  @JsonProperty("service_name")
+  public String getServiceName() {
+    return serviceName;
+  }
+
+  /**
+   * @param name the service name
+   */
+  public void setServiceName(String name) {
+    serviceName = name;
+  }
+
+  /**
+   * @return the component name
+   */
+  @JsonProperty("component_name")
+  public String getComponentName() {
+    return componentName;
+  }
+
+  /**
+   *
+   * @param name the component name
+   */
+  public void setComponentName(String name) {
+    componentName = name;
+  }
+
+  /**
+   * @return the name
+   */
+  @JsonProperty("name")
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * @param definitionName
+   *          the definition name.
+   */
+  public void setName(String definitionName) {
+    name = definitionName;
+  }
+
+  /**
+   * @return the label for the definition or {@code null} if none.
+   */
+  @JsonProperty("label")
+  public String getLabel() {
+    return label;
+  }
+
+  /**
+   * Sets the label for this definition.
+   *
+   * @param definitionLabel
+   */
+  public void setLabel(String definitionLabel) {
+    label = definitionLabel;
+  }
+
+  @Override
+  public String toString() {
+    return name;
+  }
+
+  /**
+   * Gets an {@link AlertDefinitionResponse} from the supplied entity.
+   *
+   * @param entity
+   *          the entity (not {@code null}).
+   * @return the response.
+   */
+  public static AlertDefinitionResponse coerce(AlertDefinitionEntity entity) {
+    if (null == entity) {
+      return null;
+    }
+
+    AlertDefinitionResponse response = new AlertDefinitionResponse();
+    response.setDefinitionId(entity.getDefinitionId());
+    response.setComponentName(entity.getComponentName());
+    response.setLabel(entity.getLabel());
+    response.setName(entity.getDefinitionName());
+    response.setServiceName(entity.getServiceName());
+
+    return response;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProvider.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProvider.java
index f14e76f..fc51ddd 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProvider.java
@@ -17,14 +17,17 @@
  */
 package org.apache.ambari.server.controller.internal;
 
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.AlertDefinitionResponse;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
@@ -36,11 +39,14 @@ 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;
+import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.dao.AlertDispatchDAO;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.orm.entities.AlertGroupEntity;
 import org.apache.ambari.server.orm.entities.AlertTargetEntity;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.alert.AlertGroup;
+import org.apache.ambari.server.state.alert.AlertTarget;
 import org.apache.commons.lang.StringUtils;
 
 import com.google.inject.Inject;
@@ -66,12 +72,16 @@ public class AlertGroupResourceProvider extends
       Arrays.asList(ALERT_GROUP_ID, ALERT_GROUP_CLUSTER_NAME));
 
   /**
-   * Group DAO
+   * Group/Target DAO
    */
-  @Inject
   private static AlertDispatchDAO s_dao;
 
   /**
+   * Definitions DAO
+   */
+  private static AlertDefinitionDAO s_definitionDao;
+
+  /**
    * Initializes the injectable members of this class with the specified
    * injector.
    *
@@ -81,6 +91,7 @@ public class AlertGroupResourceProvider extends
   @Inject
   public static void init(Injector injector) {
     s_dao = injector.getInstance(AlertDispatchDAO.class);
+    s_definitionDao = injector.getInstance(AlertDefinitionDAO.class);
   }
 
   /**
@@ -156,11 +167,21 @@ public class AlertGroupResourceProvider extends
   }
 
   @Override
-  public RequestStatus updateResources(Request request, Predicate predicate)
+  public RequestStatus updateResources(final Request request,
+      Predicate predicate)
       throws SystemException, UnsupportedPropertyException,
       NoSuchResourceException, NoSuchParentResourceException {
 
-    throw new UnsupportedOperationException();
+    modifyResources(new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        updateAlertGroups(request.getProperties());
+        return null;
+      }
+    });
+
+    notifyUpdate(Resource.Type.AlertGroup, request, predicate);
+    return getRequestStatus(null);
   }
 
   @Override
@@ -207,6 +228,7 @@ public class AlertGroupResourceProvider extends
    * @param requestMaps
    * @throws AmbariException
    */
+  @SuppressWarnings("unchecked")
   private void createAlertGroups(Set<Map<String, Object>> requestMaps)
       throws AmbariException {
 
@@ -231,9 +253,27 @@ public class AlertGroupResourceProvider extends
           clusterName);
 
       entity.setClusterId(cluster.getClusterId());
-      entity.setDefault(false);
       entity.setGroupName(name);
 
+      // groups created through the provider are not default service groups
+      entity.setDefault(false);
+
+      // targets are not required on creation
+      if (requestMap.containsKey(ALERT_GROUP_TARGETS)) {
+        List<Long> targetIds = (List<Long>) 
requestMap.get(ALERT_GROUP_TARGETS);
+        Set<AlertTargetEntity> targets = new HashSet<AlertTargetEntity>();
+        targets.addAll(s_dao.findTargetsById(targetIds));
+        entity.setAlertTargets(targets);
+      }
+
+      // definitions are not required on creation
+      if (requestMap.containsKey(ALERT_GROUP_DEFINITIONS)) {
+        List<Long> definitionIds = (List<Long>) 
requestMap.get(ALERT_GROUP_DEFINITIONS);
+        Set<AlertDefinitionEntity> definitions = new 
HashSet<AlertDefinitionEntity>();
+        definitions.addAll(s_definitionDao.findByIds(definitionIds));
+        entity.setAlertDefinitions(definitions);
+      }
+
       entities.add(entity);
     }
 
@@ -241,6 +281,74 @@ public class AlertGroupResourceProvider extends
   }
 
   /**
+   * Updates existing {@link AlertGroupEntity}s with the specified properties.
+   *
+   * @param requestMaps
+   *          a set of property maps, one map for each entity.
+   * @throws AmbariException
+   *           if the entity could not be found.
+   */
+  @SuppressWarnings("unchecked")
+  private void updateAlertGroups(Set<Map<String, Object>> requestMaps)
+      throws AmbariException {
+
+    for (Map<String, Object> requestMap : requestMaps) {
+      String stringId = (String) requestMap.get(ALERT_GROUP_ID);
+
+      if( StringUtils.isEmpty(stringId)){
+        throw new IllegalArgumentException("The ID of the alert group is 
required when updating an existing group");
+      }
+
+      long id = Long.parseLong(stringId);
+      AlertGroupEntity entity = s_dao.findGroupById(id);
+
+      if( null == entity ){
+        String message = MessageFormat.format("The alert group with ID {0} 
could not be found", id);
+        throw new AmbariException(message);
+      }
+
+      String name = (String) requestMap.get(ALERT_GROUP_NAME);
+
+      // empty arrays are deserialized as HashSet while populated arrays
+      // are deserialized as ArrayList; use Collection for safety
+      Collection<Long> targetIds = (Collection<Long>) 
requestMap.get(ALERT_GROUP_TARGETS);
+      Collection<Long> definitionIds = (Collection<Long>) 
requestMap.get(ALERT_GROUP_DEFINITIONS);
+
+      if (!StringUtils.isBlank(name)) {
+        entity.setGroupName(name);
+      }
+
+      // if targets were supplied, replace existing
+      Set<AlertTargetEntity> targets = new HashSet<AlertTargetEntity>();
+      if (null != targetIds && targetIds.size() > 0) {
+        List<Long> ids = new ArrayList<Long>(targetIds.size());
+        ids.addAll(targetIds);
+        targets.addAll(s_dao.findTargetsById(ids));
+
+        entity.setAlertTargets(targets);
+      } else if (targetIds.size() == 0) {
+        // empty array supplied, clear out existing targets
+        entity.setAlertTargets(targets);
+      }
+
+      // if definitions were supplied, replace existing
+      Set<AlertDefinitionEntity> definitions = new 
HashSet<AlertDefinitionEntity>();
+      if (null != definitionIds && definitionIds.size() > 0) {
+        List<Long> ids = new ArrayList<Long>(definitionIds.size());
+        ids.addAll(definitionIds);
+        definitions.addAll(s_definitionDao.findByIds(ids));
+
+        entity.setAlertDefinitions(definitions);
+      } else if (definitionIds.size() == 0) {
+        // empty array supplied, clear out existing definitions
+        entity.setAlertDefinitions(definitions);
+      }
+
+      s_dao.merge(entity);
+    }
+  }
+
+  /**
    * Convert the given {@link AlertGroupEntity} to a {@link Resource}.
    *
    * @param isCollection
@@ -259,9 +367,31 @@ public class AlertGroupResourceProvider extends
     resource.setProperty(ALERT_GROUP_ID, entity.getGroupId());
     resource.setProperty(ALERT_GROUP_NAME, entity.getGroupName());
     resource.setProperty(ALERT_GROUP_CLUSTER_NAME, clusterName);
+    resource.setProperty(ALERT_GROUP_DEFAULT, entity.isDefault());
+
+    if( !isCollection ){
+      Set<AlertTargetEntity> targetEntities = entity.getAlertTargets();
+      Set<AlertDefinitionEntity> definitions = entity.getAlertDefinitions();
+
+      List<AlertTarget> targets = new ArrayList<AlertTarget>(
+          targetEntities.size());
 
-    setResourceProperty(resource, ALERT_GROUP_DEFAULT,
-        entity.isDefault(), requestedIds);
+      List<AlertDefinitionResponse> definitionList = new 
ArrayList<AlertDefinitionResponse>(
+          definitions.size());
+
+      for (AlertTargetEntity targetEntity : targetEntities) {
+        AlertTarget target = AlertTarget.coerce(targetEntity);
+        targets.add(target);
+      }
+
+      for (AlertDefinitionEntity definition : definitions) {
+        AlertDefinitionResponse response = 
AlertDefinitionResponse.coerce(definition);
+        definitionList.add(response);
+      }
+
+      resource.setProperty(ALERT_GROUP_DEFINITIONS, definitionList);
+      resource.setProperty(ALERT_GROUP_TARGETS, targets);
+    }
 
     return resource;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java
index 4a8fe12..f410c39 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java
@@ -124,6 +124,22 @@ public class AlertDefinitionDAO {
   }
 
   /**
+   * Gets all of the alert definitions for the list of IDs given.
+   * 
+   * @param definitionIds
+   *          the IDs of the definitions to retrieve.
+   * @return the definition or an empty list (never {@code null}).
+   */
+  public List<AlertDefinitionEntity> findByIds(List<Long> definitionIds) {
+    TypedQuery<AlertDefinitionEntity> query = 
entityManagerProvider.get().createNamedQuery(
+        "AlertDefinitionEntity.findByIds", AlertDefinitionEntity.class);
+
+    query.setParameter("definitionIds", definitionIds);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
    * Gets all alert definitions for the given service in the specified cluster.
    *
    * @param clusterId

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDispatchDAO.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDispatchDAO.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDispatchDAO.java
index dfbe747..e08c948 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDispatchDAO.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDispatchDAO.java
@@ -72,6 +72,22 @@ public class AlertDispatchDAO {
   }
 
   /**
+   * Gets all of the alert targets for the list of IDs given.
+   *
+   * @param targetIds
+   *          the IDs of the targets to retrieve.
+   * @return the targets or an empty list (never {@code null}).
+   */
+  public List<AlertTargetEntity> findTargetsById(List<Long> targetIds) {
+    TypedQuery<AlertTargetEntity> query = 
entityManagerProvider.get().createNamedQuery(
+        "AlertTargetEntity.findByIds", AlertTargetEntity.class);
+
+    query.setParameter("targetIds", targetIds);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
    * Gets a notification with the specified ID.
    *
    * @param noticeId
@@ -207,7 +223,7 @@ public class AlertDispatchDAO {
 
   /**
    * Persists a new alert group.
-   * 
+   *
    * @param alertGroup
    *          the group to persist (not {@code null}).
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/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 299467c..b4b8a44 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
@@ -17,6 +17,7 @@
  */
 package org.apache.ambari.server.orm.entities;
 
+import java.util.HashSet;
 import java.util.Set;
 
 import javax.persistence.Basic;
@@ -58,7 +59,8 @@ import org.apache.ambari.server.state.alert.Scope;
     @NamedQuery(name = "AlertDefinitionEntity.findByName", query = "SELECT ad 
FROM AlertDefinitionEntity ad WHERE ad.definitionName = :definitionName AND 
ad.clusterId = :clusterId"),
     @NamedQuery(name = "AlertDefinitionEntity.findByService", query = "SELECT 
ad FROM AlertDefinitionEntity ad WHERE ad.serviceName = :serviceName AND 
ad.clusterId = :clusterId"),
     @NamedQuery(name = "AlertDefinitionEntity.findByServiceAndComponent", 
query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.serviceName = 
:serviceName AND ad.componentName = :componentName AND ad.clusterId = 
:clusterId"),
-    @NamedQuery(name = "AlertDefinitionEntity.findByServiceMaster", query = 
"SELECT ad FROM AlertDefinitionEntity ad WHERE ad.serviceName IN :services AND 
ad.scope = :scope AND ad.clusterId = :clusterId") })
+    @NamedQuery(name = "AlertDefinitionEntity.findByServiceMaster", query = 
"SELECT ad FROM AlertDefinitionEntity ad WHERE ad.serviceName IN :services AND 
ad.scope = :scope AND ad.clusterId = :clusterId"),
+    @NamedQuery(name = "AlertDefinitionEntity.findByIds", query = "SELECT ad 
FROM AlertDefinitionEntity ad WHERE ad.definitionId IN :definitionIds") })
 public class AlertDefinitionEntity {
 
   @Id
@@ -394,6 +396,34 @@ public class AlertDefinitionEntity {
   }
 
   /**
+   * Adds the specified alert group to the groups that this definition is
+   * associated with. This is used to complement the JPA bidirectional
+   * association.
+   *
+   * @param alertGroup
+   */
+  protected void addAlertGroup(AlertGroupEntity alertGroup) {
+    if (null == alertGroups) {
+      alertGroups = new HashSet<AlertGroupEntity>();
+    }
+
+    alertGroups.add(alertGroup);
+  }
+
+  /**
+   * Removes the specified alert group to the groups that this definition is
+   * associated with. This is used to complement the JPA bidirectional
+   * association.
+   *
+   * @param alertGroup
+   */
+  protected void removeAlertGroup(AlertGroupEntity alertGroup) {
+    if (null != alertGroups) {
+      alertGroups.remove(alertGroup);
+    }
+  }
+
+  /**
    * Called before {@link EntityManager#remove(Object)} for this entity, 
removes
    * the non-owning relationship between definitions and groups.
    */
@@ -405,10 +435,7 @@ public class AlertDefinitionEntity {
     }
 
     for (AlertGroupEntity group : groups) {
-      Set<AlertDefinitionEntity> definitions = group.getAlertDefinitions();
-      if (null != definitions) {
-        definitions.remove(this);
-      }
+      group.removeAlertDefinition(this);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertGroupEntity.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertGroupEntity.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertGroupEntity.java
index 1ca592c..f97a0eb 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertGroupEntity.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertGroupEntity.java
@@ -177,7 +177,7 @@ public class AlertGroupEntity {
   /**
    * Set the service name. This is only applicable when {@link #isDefault()} is
    * {@code true}.
-   * 
+   *
    * @param serviceName
    *          the service that this is the default group for, or {@code null} 
if
    *          this is not a default group.
@@ -189,10 +189,14 @@ public class AlertGroupEntity {
   /**
    * Gets all of the alert definitions that are a part of this grouping.
    *
-   * @return the alert definitions or {@code null} if none.
+   * @return the alert definitions or an empty set if none (never {@code null).
    */
   public Set<AlertDefinitionEntity> getAlertDefinitions() {
-    return alertDefinitions;
+    if (null == alertDefinitions) {
+      alertDefinitions = new HashSet<AlertDefinitionEntity>();
+    }
+
+    return Collections.unmodifiableSet(alertDefinitions);
   }
 
   /**
@@ -202,14 +206,58 @@ public class AlertGroupEntity {
    *          the definitions, or {@code null} for none.
    */
   public void setAlertDefinitions(Set<AlertDefinitionEntity> alertDefinitions) 
{
+    if (null != this.alertDefinitions) {
+      for (AlertDefinitionEntity definition : this.alertDefinitions) {
+        definition.removeAlertGroup(this);
+      }
+    }
+
     this.alertDefinitions = alertDefinitions;
+
+    if (null != alertDefinitions) {
+      for (AlertDefinitionEntity definition : alertDefinitions) {
+        definition.addAlertGroup(this);
+      }
+    }
+  }
+
+  /**
+   * Adds the specified definition to the definitions that this group will
+   * dispatch to.
+   * 
+   * @param definition
+   *          the definition to add (not {@code null}).
+   */
+  public void addAlertDefinition(AlertDefinitionEntity definition) {
+    if (null == alertDefinitions) {
+      alertDefinitions = new HashSet<AlertDefinitionEntity>();
+    }
+
+    alertDefinitions.add(definition);
+    definition.addAlertGroup(this);
+  }
+
+  /**
+   * Removes the specified definition from the definitions that this group will
+   * dispatch to.
+   * 
+   * @param definition
+   *          the definition to remove (not {@code null}).
+   */
+  public void removeAlertDefinition(AlertDefinitionEntity definition) {
+    if (null != alertDefinitions) {
+      alertDefinitions.remove(definition);
+    }
+
+    definition.removeAlertGroup(this);
   }
 
   /**
    * Gets an immutable set of the targets that will receive notifications for
    * alert definitions in this group.
    *
-   * @return the targets, or {@code null} if there are none.
+   * @return the targets that will be dispatch to for alerts in this group, or
+   *         an empty set if there are none (never {@code null}).
    */
   public Set<AlertTargetEntity> getAlertTargets() {
     if( null == alertTargets ) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java
index 89728e5..12c394d 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java
@@ -46,7 +46,8 @@ import javax.persistence.TableGenerator;
 @TableGenerator(name = "alert_target_id_generator", table = 
"ambari_sequences", pkColumnName = "sequence_name", valueColumnName = 
"sequence_value", pkColumnValue = "alert_target_id_seq", initialValue = 0, 
allocationSize = 1)
 @NamedQueries({
     @NamedQuery(name = "AlertTargetEntity.findAll", query = "SELECT 
alertTarget FROM AlertTargetEntity alertTarget"),
-    @NamedQuery(name = "AlertTargetEntity.findByName", query = "SELECT 
alertTarget FROM AlertTargetEntity alertTarget WHERE alertTarget.targetName = 
:targetName"), })
+    @NamedQuery(name = "AlertTargetEntity.findByName", query = "SELECT 
alertTarget FROM AlertTargetEntity alertTarget WHERE alertTarget.targetName = 
:targetName"),
+    @NamedQuery(name = "AlertTargetEntity.findByIds", query = "SELECT 
alertTarget FROM AlertTargetEntity alertTarget WHERE alertTarget.targetId IN 
:targetIds") })
 public class AlertTargetEntity {
 
   @Id

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertTarget.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertTarget.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertTarget.java
index 21e83d9..f495787 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertTarget.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertTarget.java
@@ -17,24 +17,27 @@
  */
 package org.apache.ambari.server.state.alert;
 
-import java.util.List;
+import org.apache.ambari.server.orm.entities.AlertTargetEntity;
+import org.codehaus.jackson.annotate.JsonProperty;
+
 
 /**
  * The {@link AlertTarget} class represents a dispatch mechanism and audience
  * that will receive information about alerts int he system.
  */
 public class AlertTarget {
-  private String m_id;
+
+  private Long m_id;
   private String m_name;
   private String m_description;
   private String m_notificationType;
   private String m_properties;
-  private List<AlertGroup> m_groups;
 
   /**
    * @return the id
    */
-  public String getId() {
+  @JsonProperty("id")
+  public Long getId() {
     return m_id;
   }
 
@@ -42,13 +45,14 @@ public class AlertTarget {
    * @param id
    *          the id to set
    */
-  public void setId(String id) {
+  public void setId(Long id) {
     m_id = id;
   }
 
   /**
    * @return the name
    */
+  @JsonProperty("name")
   public String getName() {
     return m_name;
   }
@@ -64,6 +68,7 @@ public class AlertTarget {
   /**
    * @return the description
    */
+  @JsonProperty("description")
   public String getDescription() {
     return m_description;
   }
@@ -79,6 +84,7 @@ public class AlertTarget {
   /**
    * @return the notificationType
    */
+  @JsonProperty("notification_type")
   public String getNotificationType() {
     return m_notificationType;
   }
@@ -94,6 +100,7 @@ public class AlertTarget {
   /**
    * @return the properties
    */
+  @JsonProperty("properties")
   public String getProperties() {
     return m_properties;
   }
@@ -107,17 +114,17 @@ public class AlertTarget {
   }
 
   /**
-   * @return the groups
+   * @param entity
+   * @return
    */
-  public List<AlertGroup> getGroups() {
-    return m_groups;
-  }
+  public static AlertTarget coerce(AlertTargetEntity entity) {
+    AlertTarget target = new AlertTarget();
+    target.setId(entity.getTargetId());
+    target.setDescription(entity.getDescription());
+    target.setName(entity.getTargetName());
+    target.setNotificationType(entity.getNotificationType());
+    target.setProperties(entity.getProperties());
 
-  /**
-   * @param groups
-   *          the groups to set
-   */
-  public void setGroups(List<AlertGroup> groups) {
-    m_groups = groups;
+    return target;
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProviderTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProviderTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProviderTest.java
index c9428b7..ddfc75a 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProviderTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertGroupResourceProviderTest.java
@@ -17,24 +17,30 @@
  */
 package org.apache.ambari.server.controller.internal;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
 import static org.easymock.EasyMock.anyObject;
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.createStrictMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.resetToStrict;
 import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.assertEquals;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 
+import org.apache.ambari.server.controller.AlertDefinitionResponse;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.Request;
@@ -43,13 +49,18 @@ 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.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.dao.AlertDispatchDAO;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.orm.entities.AlertGroupEntity;
+import org.apache.ambari.server.orm.entities.AlertTargetEntity;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.alert.AlertTarget;
+import org.apache.ambari.server.state.alert.SourceType;
+import org.apache.ambari.server.state.alert.TargetType;
 import org.easymock.Capture;
 import org.easymock.EasyMock;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -69,17 +80,42 @@ public class AlertGroupResourceProviderTest {
   private static final long ALERT_GROUP_CLUSTER_ID = 1L;
   private static final String ALERT_GROUP_CLUSTER_NAME = "c1";
 
+  private static final Long ALERT_TARGET_ID = Long.valueOf(28);
+  private static final String ALERT_TARGET_NAME = "The Administrators";
+  private static final String ALERT_TARGET_DESC = "Admins and Others";
+  private static final String ALERT_TARGET_TYPE = TargetType.EMAIL.name();
+
+  private static final Long ALERT_DEF_ID = Long.valueOf(10);
+  private static final String ALERT_DEF_NAME = "Mock Definition";
+  private static final String ALERT_DEF_LABEL = "Mock Label";
+
+  private static String DEFINITION_UUID = UUID.randomUUID().toString();
+
   private AlertDispatchDAO m_dao;
+  private AlertDefinitionDAO m_definitionDao;
   private Injector m_injector;
 
+  private AmbariManagementController m_amc;
+  private Clusters m_clusters;
+  private Cluster m_cluster;
+
   @Before
-  public void before() {
-    m_dao = createStrictMock(AlertDispatchDAO.class);
+  public void before() throws Exception {
+    m_dao = createMock(AlertDispatchDAO.class);
+    m_definitionDao = createMock(AlertDefinitionDAO.class);
+
+    m_amc = createMock(AmbariManagementController.class);
+    m_clusters = createMock(Clusters.class);
+    m_cluster = createMock(Cluster.class);
 
     m_injector = Guice.createInjector(Modules.override(
         new InMemoryDefaultTestModule()).with(new MockModule()));
 
     AlertGroupResourceProvider.init(m_injector);
+
+    expect(m_amc.getClusters()).andReturn(m_clusters).atLeastOnce();
+    expect(m_clusters.getCluster((String) 
anyObject())).andReturn(m_cluster).atLeastOnce();
+    expect(m_cluster.getClusterId()).andReturn(Long.valueOf(1)).anyTimes();
   }
 
   /**
@@ -108,44 +144,41 @@ public class AlertGroupResourceProviderTest {
         AlertGroupResourceProvider.ALERT_GROUP_CLUSTER_NAME,
         AlertGroupResourceProvider.ALERT_GROUP_DEFAULT);
 
-    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(
         
AlertGroupResourceProvider.ALERT_GROUP_CLUSTER_NAME).equals("c1").toPredicate();
 
     expect(m_dao.findAllGroups(ALERT_GROUP_CLUSTER_ID)).andReturn(
         getMockEntities());
 
-    replay(amc, clusters, cluster, m_dao);
+    replay(m_amc, m_clusters, m_cluster, m_dao);
 
-    AlertGroupResourceProvider provider = createProvider(amc);
+    AlertGroupResourceProvider provider = createProvider(m_amc);
     Set<Resource> results = provider.getResources(request, predicate);
 
     assertEquals(1, results.size());
 
     Resource r = results.iterator().next();
 
-    Assert.assertEquals(ALERT_GROUP_NAME,
+    assertEquals(ALERT_GROUP_NAME,
         r.getPropertyValue(AlertGroupResourceProvider.ALERT_GROUP_NAME));
 
-    Assert.assertEquals(ALERT_GROUP_ID,
+    assertEquals(ALERT_GROUP_ID,
         r.getPropertyValue(AlertGroupResourceProvider.ALERT_GROUP_ID));
 
-    Assert.assertEquals(ALERT_GROUP_CLUSTER_NAME,
+    assertEquals(ALERT_GROUP_CLUSTER_NAME,
         
r.getPropertyValue(AlertGroupResourceProvider.ALERT_GROUP_CLUSTER_NAME));
 
-    verify(amc, clusters, cluster, m_dao);
+    // verify definitions do not come back in collections
+    
assertNull(r.getPropertyValue(AlertGroupResourceProvider.ALERT_GROUP_DEFINITIONS));
+
+    verify(m_amc, m_clusters, m_cluster, m_dao);
   }
 
   /**
    * @throws Exception
    */
   @Test
+  @SuppressWarnings("unchecked")
   public void testGetSingleResource() throws Exception {
     Request request = PropertyHelper.getReadRequest(
         AlertGroupResourceProvider.ALERT_GROUP_ID,
@@ -173,15 +206,28 @@ public class AlertGroupResourceProviderTest {
 
     Resource r = results.iterator().next();
 
-    Assert.assertEquals(ALERT_GROUP_NAME,
+    assertEquals(ALERT_GROUP_NAME,
         r.getPropertyValue(AlertGroupResourceProvider.ALERT_GROUP_NAME));
 
-    Assert.assertEquals(ALERT_GROUP_ID,
+    assertEquals(ALERT_GROUP_ID,
         r.getPropertyValue(AlertGroupResourceProvider.ALERT_GROUP_ID));
 
-    Assert.assertEquals(ALERT_GROUP_CLUSTER_NAME,
+    assertEquals(ALERT_GROUP_CLUSTER_NAME,
         
r.getPropertyValue(AlertGroupResourceProvider.ALERT_GROUP_CLUSTER_NAME));
 
+    // verify definitions and targets are returned on single instances
+    List<AlertDefinitionResponse> definitions = 
(List<AlertDefinitionResponse>) 
r.getPropertyValue(AlertGroupResourceProvider.ALERT_GROUP_DEFINITIONS);
+    List<AlertTarget> targets = (List<AlertTarget>) 
r.getPropertyValue(AlertGroupResourceProvider.ALERT_GROUP_TARGETS);
+
+    assertNotNull(definitions);
+    assertNotNull(targets);
+
+    assertEquals(1, definitions.size());
+    assertEquals(ALERT_DEF_NAME, definitions.get(0).getName());
+
+    assertEquals(1, targets.size());
+    assertEquals(ALERT_TARGET_NAME, targets.get(0).getName());
+
     verify(amc, m_dao);
   }
 
@@ -190,21 +236,40 @@ public class AlertGroupResourceProviderTest {
    */
   @Test
   public void testCreateResources() throws Exception {
-    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();
-
     Capture<List<AlertGroupEntity>> listCapture = new 
Capture<List<AlertGroupEntity>>();
 
+    // the definition IDs to associate with the group
+    List<Long> definitionIds = new ArrayList<Long>();
+    definitionIds.add(ALERT_DEF_ID);
+
+    // the target IDs to associate with the group
+    List<Long> targetIds = new ArrayList<Long>();
+    targetIds.add(ALERT_TARGET_ID);
+
+    // definition entities to return from DAO
+    List<AlertDefinitionEntity> definitionEntities = new 
ArrayList<AlertDefinitionEntity>();
+    definitionEntities.addAll(getMockDefinitions());
+
+    // target entities to return from DAO
+    List<AlertTargetEntity> targetEntities = new 
ArrayList<AlertTargetEntity>();
+    targetEntities.addAll(getMockTargets());
+
+    // expect create group
     m_dao.createGroups(capture(listCapture));
-    expectLastCall();
+    expectLastCall().once();
 
-    replay(amc, clusters, cluster, m_dao);
+    // expect target entity lookup for association
+    expect(m_dao.findTargetsById(EasyMock.eq(targetIds))).andReturn(
+        targetEntities).times(1);
+
+    // expect definition entity lookup for association
+    expect(m_definitionDao.findByIds(definitionIds)).andReturn(
+        definitionEntities).times(1);
+
+    replay(m_amc, m_clusters, m_cluster, m_dao, m_definitionDao);
+
+    AlertGroupResourceProvider provider = createProvider(m_amc);
 
-    AlertGroupResourceProvider provider = createProvider(amc);
     Map<String, Object> requestProps = new HashMap<String, Object>();
     requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_NAME,
         ALERT_GROUP_NAME);
@@ -212,25 +277,109 @@ public class AlertGroupResourceProviderTest {
     requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_CLUSTER_NAME,
         ALERT_GROUP_CLUSTER_NAME);
 
+    requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_DEFINITIONS,
+        definitionIds);
+
+    requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_TARGETS, 
targetIds);
+
     Request request = 
PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
     provider.createResources(request);
 
-    Assert.assertTrue(listCapture.hasCaptured());
+    assertTrue(listCapture.hasCaptured());
     AlertGroupEntity entity = listCapture.getValue().get(0);
-    Assert.assertNotNull(entity);
+    assertNotNull(entity);
 
-    Assert.assertEquals(ALERT_GROUP_NAME, entity.getGroupName());
-    Assert.assertEquals(ALERT_GROUP_CLUSTER_ID,
+    assertEquals(ALERT_GROUP_NAME, entity.getGroupName());
+    assertEquals(ALERT_GROUP_CLUSTER_ID,
         entity.getClusterId().longValue());
 
-    verify(amc, clusters, cluster, m_dao);
+    verify(m_amc, m_clusters, m_cluster, m_dao, m_definitionDao);
   }
 
   /**
    * @throws Exception
    */
   @Test
+  @SuppressWarnings("unchecked")
   public void testUpdateResources() throws Exception {
+    Capture<AlertGroupEntity> entityCapture = new Capture<AlertGroupEntity>();
+
+    // the definition IDs to associate with the group
+    List<Long> definitionIds = new ArrayList<Long>();
+    definitionIds.add(ALERT_DEF_ID);
+
+    // the target IDs to associate with the group
+    List<Long> targetIds = new ArrayList<Long>();
+    targetIds.add(ALERT_TARGET_ID);
+
+    // definition entities to return from DAO
+    List<AlertDefinitionEntity> definitionEntities = new 
ArrayList<AlertDefinitionEntity>();
+    definitionEntities.addAll(getMockDefinitions());
+
+    // target entities to return from DAO
+    List<AlertTargetEntity> targetEntities = new 
ArrayList<AlertTargetEntity>();
+    targetEntities.addAll(getMockTargets());
+
+    m_dao.createGroups(EasyMock.anyObject(List.class));
+    expectLastCall().times(1);
+
+    AlertGroupEntity group = new AlertGroupEntity();
+    expect(m_dao.findGroupById(ALERT_GROUP_ID)).andReturn(
+        group).times(1);
+
+    expect(m_dao.merge(capture(entityCapture))).andReturn(group).once();
+
+    // expect target entity lookup for association
+    expect(m_dao.findTargetsById(EasyMock.eq(targetIds))).andReturn(
+        targetEntities).once();
+
+    // expect definition entity lookup for association
+    expect(m_definitionDao.findByIds(definitionIds)).andReturn(
+        definitionEntities).once();
+
+    replay(m_amc, m_clusters, m_cluster, m_dao, m_definitionDao);
+
+    AlertGroupResourceProvider provider = createProvider(m_amc);
+    Map<String, Object> requestProps = new HashMap<String, Object>();
+    requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_NAME,
+        ALERT_GROUP_NAME);
+
+    requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_CLUSTER_NAME,
+        ALERT_GROUP_CLUSTER_NAME);
+
+    Request request = PropertyHelper.getCreateRequest(
+        Collections.singleton(requestProps), null);
+
+    provider.createResources(request);
+
+    // create new properties, and include the ID since we're not going through
+    // a service layer which would add it for us automatically
+    requestProps = new HashMap<String, Object>();
+    requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_ID,
+        ALERT_GROUP_ID.toString());
+
+    String newName = ALERT_GROUP_NAME + " Foo";
+    requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_NAME, newName);
+
+    requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_DEFINITIONS,
+        definitionIds);
+
+    requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_TARGETS, 
targetIds);
+
+    Predicate predicate = new PredicateBuilder().property(
+        AlertGroupResourceProvider.ALERT_GROUP_CLUSTER_NAME).equals(
+        ALERT_GROUP_CLUSTER_NAME).and().property(
+        AlertGroupResourceProvider.ALERT_GROUP_ID).equals(
+        ALERT_GROUP_ID.toString()).toPredicate();
+
+    request = PropertyHelper.getUpdateRequest(requestProps, null);
+    provider.updateResources(request, predicate);
+
+    assertTrue(entityCapture.hasCaptured());
+
+    AlertGroupEntity entity = entityCapture.getValue();
+    assertEquals(newName, entity.getGroupName());
+    verify(m_amc, m_clusters, m_cluster, m_dao, m_definitionDao);
   }
 
   /**
@@ -238,22 +387,15 @@ public class AlertGroupResourceProviderTest {
    */
   @Test
   public void testDeleteResources() throws Exception {
-    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();
-
     Capture<AlertGroupEntity> entityCapture = new Capture<AlertGroupEntity>();
     Capture<List<AlertGroupEntity>> listCapture = new 
Capture<List<AlertGroupEntity>>();
 
     m_dao.createGroups(capture(listCapture));
     expectLastCall();
 
-    replay(amc, clusters, cluster, m_dao);
+    replay(m_amc, m_clusters, m_cluster, m_dao);
 
-    AlertGroupResourceProvider provider = createProvider(amc);
+    AlertGroupResourceProvider provider = createProvider(m_amc);
 
     Map<String, Object> requestProps = new HashMap<String, Object>();
     requestProps.put(AlertGroupResourceProvider.ALERT_GROUP_NAME,
@@ -265,9 +407,9 @@ public class AlertGroupResourceProviderTest {
     Request request = 
PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
     provider.createResources(request);
 
-    Assert.assertTrue(listCapture.hasCaptured());
+    assertTrue(listCapture.hasCaptured());
     AlertGroupEntity entity = listCapture.getValue().get(0);
-    Assert.assertNotNull(entity);
+    assertNotNull(entity);
 
     Predicate predicate = new PredicateBuilder().property(
         AlertGroupResourceProvider.ALERT_GROUP_CLUSTER_NAME).equals(
@@ -287,9 +429,9 @@ public class AlertGroupResourceProviderTest {
     provider.deleteResources(predicate);
 
     AlertGroupEntity entity1 = entityCapture.getValue();
-    Assert.assertEquals(ALERT_GROUP_ID, entity1.getGroupId());
+    assertEquals(ALERT_GROUP_ID, entity1.getGroupId());
 
-    verify(amc, clusters, cluster, m_dao);
+    verify(m_amc, m_clusters, m_cluster, m_dao);
   }
 
   /**
@@ -298,6 +440,7 @@ public class AlertGroupResourceProviderTest {
    */
   private AlertGroupResourceProvider createProvider(
       AmbariManagementController amc) {
+
     return new AlertGroupResourceProvider(
         PropertyHelper.getPropertyIds(Resource.Type.AlertGroup),
         PropertyHelper.getKeyPropertyIds(Resource.Type.AlertGroup), amc);
@@ -312,10 +455,57 @@ public class AlertGroupResourceProviderTest {
     entity.setGroupName(ALERT_GROUP_NAME);
     entity.setClusterId(ALERT_GROUP_CLUSTER_ID);
     entity.setDefault(false);
+
+    entity.setAlertTargets(getMockTargets());
+    entity.setAlertDefinitions(getMockDefinitions());
     return Arrays.asList(entity);
   }
 
   /**
+   * Gets some mock {@link AlertDefinitionEntity} instances.
+   *
+   * @return
+   * @throws Exception
+   */
+  private Set<AlertDefinitionEntity> getMockDefinitions() throws Exception {
+    AlertDefinitionEntity entity = new AlertDefinitionEntity();
+    entity.setClusterId(Long.valueOf(1L));
+    entity.setComponentName(null);
+    entity.setDefinitionId(ALERT_DEF_ID);
+    entity.setDefinitionName(ALERT_DEF_NAME);
+    entity.setLabel(ALERT_DEF_LABEL);
+    entity.setEnabled(true);
+    entity.setHash(DEFINITION_UUID);
+    entity.setScheduleInterval(Integer.valueOf(2));
+    entity.setServiceName(null);
+    entity.setSourceType(SourceType.METRIC.name());
+    entity.setSource(null);
+
+    Set<AlertDefinitionEntity> definitions = new 
HashSet<AlertDefinitionEntity>();
+    definitions.add(entity);
+
+    return definitions;
+  }
+
+  /**
+   * Gets some mock {@link AlertTargetEntity} instances.
+   *
+   * @return
+   */
+  private Set<AlertTargetEntity> getMockTargets() throws Exception {
+    AlertTargetEntity entity = new AlertTargetEntity();
+    entity.setTargetId(ALERT_TARGET_ID);
+    entity.setDescription(ALERT_TARGET_DESC);
+    entity.setTargetName(ALERT_TARGET_NAME);
+    entity.setNotificationType(ALERT_TARGET_TYPE);
+
+    Set<AlertTargetEntity> targets = new HashSet<AlertTargetEntity>();
+    targets.add(entity);
+
+    return targets;
+  }
+
+  /**
   *
   */
   private class MockModule implements Module {
@@ -325,10 +515,9 @@ public class AlertGroupResourceProviderTest {
     @Override
     public void configure(Binder binder) {
       binder.bind(AlertDispatchDAO.class).toInstance(m_dao);
-      binder.bind(Clusters.class).toInstance(
-          EasyMock.createNiceMock(Clusters.class));
-      binder.bind(Cluster.class).toInstance(
-          EasyMock.createNiceMock(Cluster.class));
+      binder.bind(AlertDefinitionDAO.class).toInstance(m_definitionDao);
+      binder.bind(Clusters.class).toInstance(m_clusters);
+      binder.bind(Cluster.class).toInstance(m_cluster);
       binder.bind(ActionMetadata.class);
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
index 982dd4b..6ca2035 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
@@ -266,7 +266,7 @@ public class AlertTargetResourceProviderTest {
     entity.setTargetId(ALERT_TARGET_ID);
     entity.setDescription(ALERT_TARGET_DESC);
     entity.setTargetName(ALERT_TARGET_NAME);
-    entity.setNotificationType(TargetType.EMAIL.name());
+    entity.setNotificationType(ALERT_TARGET_TYPE);
     return Arrays.asList(entity);
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
index a7ef513..a7cc066 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
@@ -193,6 +194,21 @@ public class AlertDefinitionDAOTest {
   }
 
   /**
+  *
+  */
+  @Test
+  public void testFindByIds() {
+    List<AlertDefinitionEntity> definitions = dao.findAll();
+    List<Long> ids = new ArrayList<Long>();
+    ids.add(definitions.get(0).getDefinitionId());
+    ids.add(definitions.get(1).getDefinitionId());
+    ids.add(99999L);
+
+    definitions = dao.findByIds(ids);
+    assertEquals(2, definitions.size());
+  }
+
+  /**
    *
    */
   @Test
@@ -260,7 +276,7 @@ public class AlertDefinitionDAOTest {
     AlertDefinitionEntity definition = helper.createAlertDefinition(clusterId);
 
     AlertGroupEntity group = helper.createAlertGroup(clusterId, null);
-    group.getAlertDefinitions().add(definition);
+    group.addAlertDefinition(definition);
     dispatchDao.merge(group);
 
     AlertHistoryEntity history = new AlertHistoryEntity();

http://git-wip-us.apache.org/repos/asf/ambari/blob/76fc94ec/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java
index 894f2c2..8451c9b 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDispatchDAOTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -56,7 +57,7 @@ public class AlertDispatchDAOTest {
   OrmTestHelper helper;
 
   /**
-   * 
+   *
    */
   @Before
   public void setup() throws Exception {
@@ -92,7 +93,7 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testFindAllTargets() throws Exception {
@@ -102,7 +103,24 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   * @throws Exception
+   */
+  public void testFindTargetsByIds() throws Exception {
+    List<AlertTargetEntity> targets = dao.findAllTargets();
+    assertNotNull(targets);
+    assertEquals(5, targets.size());
+
+    List<Long> ids = new ArrayList<Long>();
+    ids.add(targets.get(0).getTargetId());
+    ids.add(targets.get(1).getTargetId());
+    ids.add(99999L);
+
+    targets = dao.findTargetsById(ids);
+    assertEquals(2, targets.size());
+  }
+
+  /**
+   *
    */
   @Test
   public void testFindTargetByName() throws Exception {
@@ -115,7 +133,7 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testFindAllGroups() throws Exception {
@@ -125,7 +143,7 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testFindGroupByName() throws Exception {
@@ -140,7 +158,7 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testCreateGroup() throws Exception {
@@ -159,7 +177,7 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testGroupDefinitions() throws Exception {
@@ -170,7 +188,10 @@ public class AlertDispatchDAOTest {
     group = dao.findGroupById(group.getGroupId());
     assertNotNull(group);
 
-    group.getAlertDefinitions().addAll(definitions);
+    for (AlertDefinitionEntity definition : definitions) {
+      group.addAlertDefinition(definition);
+    }
+
     dao.merge(group);
 
     group = dao.findGroupByName(group.getGroupName());
@@ -193,7 +214,7 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testCreateTarget() throws Exception {
@@ -222,12 +243,12 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testDeleteGroup() throws Exception {
-    int targetCount = dao.findAllTargets().size();    
-    
+    int targetCount = dao.findAllTargets().size();
+
     AlertGroupEntity group = helper.createAlertGroup(clusterId, null);
     AlertTargetEntity target = helper.createAlertTarget();
     assertEquals(targetCount + 1, dao.findAllTargets().size());
@@ -255,7 +276,7 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testDeleteTarget() throws Exception {
@@ -270,7 +291,7 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testDeleteAssociatedTarget() throws Exception {
@@ -298,7 +319,7 @@ public class AlertDispatchDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testUpdateGroup() throws Exception {
@@ -319,8 +340,6 @@ public class AlertDispatchDAOTest {
 
     assertEquals(groupName + "FOO", group.getGroupName());
     assertEquals(true, group.isDefault());
-    assertNotNull(group.getAlertDefinitions());
-    assertNotNull(group.getAlertTargets());
     assertEquals(0, group.getAlertDefinitions().size());
     assertEquals(0, group.getAlertTargets().size());
 

Reply via email to