You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2014/09/01 13:38:53 UTC

git commit: AMBARI-7094 - Alerts: Implement Property Updates for Alert Targets (jonathanhurley)

Repository: ambari
Updated Branches:
  refs/heads/branch-alerts-dev 52d1af763 -> 2f5c78e7f


AMBARI-7094 - Alerts: Implement Property Updates for Alert Targets (jonathanhurley)


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

Branch: refs/heads/branch-alerts-dev
Commit: 2f5c78e7f90f575ab2c469da1095eaa5d252d872
Parents: 52d1af7
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Sun Aug 31 08:25:14 2014 -0400
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Sun Aug 31 08:25:14 2014 -0400

----------------------------------------------------------------------
 .../resources/AlertDefResourceDefinition.java   |   3 -
 .../server/api/services/AlertTargetService.java |  10 ++
 .../services/parsers/JsonRequestBodyParser.java |  14 +-
 .../internal/AlertTargetResourceProvider.java   | 134 ++++++++++++++++--
 .../ambari/server/state/ConfigFactory.java      |   2 -
 .../ambari/server/state/alert/AlertTarget.java  |  10 +-
 .../AlertTargetResourceProviderTest.java        | 140 +++++++++++++------
 7 files changed, 249 insertions(+), 64 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/2f5c78e7/ambari-server/src/main/java/org/apache/ambari/server/api/resources/AlertDefResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/AlertDefResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/AlertDefResourceDefinition.java
index 1a63097..66e5a83 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/AlertDefResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/AlertDefResourceDefinition.java
@@ -21,8 +21,6 @@ import org.apache.ambari.server.controller.spi.Resource;
 
 /**
  * Resource Definition for AlertDefinition types.
- * @author ncole
- *
  */
 public class AlertDefResourceDefinition extends BaseResourceDefinition {
 
@@ -32,7 +30,6 @@ public class AlertDefResourceDefinition extends BaseResourceDefinition {
   
   @Override
   public String getPluralName() {
-    // TODO Auto-generated method stub
     return "alert_definitions";
   }
   

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f5c78e7/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertTargetService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertTargetService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertTargetService.java
index 86281b3..2a2ecdf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertTargetService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/AlertTargetService.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;
@@ -67,6 +68,15 @@ public class AlertTargetService extends BaseService {
         createAlertTargetResource(null));
   }
 
+  @PUT
+  @Produces("text/plain")
+  @Path("{targetId}")
+  public Response updateGroup(String body, @Context HttpHeaders headers,
+      @Context UriInfo ui, @PathParam("targetId") Long targetId) {
+    return handleRequest(headers, body, ui, Request.Type.PUT,
+        createAlertTargetResource(targetId));
+  }
+
   @DELETE
   @Produces("text/plain")
   @Path("{targetId}")

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f5c78e7/ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonRequestBodyParser.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonRequestBodyParser.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonRequestBodyParser.java
index 59dd1af..bbe7647 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonRequestBodyParser.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonRequestBodyParser.java
@@ -18,6 +18,15 @@
 
 package org.apache.ambari.server.api.services.parsers;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.ambari.server.api.services.NamedPropertySet;
 import org.apache.ambari.server.api.services.RequestBody;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
@@ -26,9 +35,6 @@ import org.codehaus.jackson.map.ObjectMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.util.*;
-
 /**
  * JSON parser which parses a JSON string into a map of properties and values.
  */
@@ -120,7 +126,7 @@ public class JsonRequestBodyParser implements RequestBodyParser {
 
           if (next.isValueNode()) {
             // All remain nodes will be also primitives
-            primitives.add(next.getTextValue());
+            primitives.add(next.asText());
           } else {
             NamedPropertySet arrayPropertySet = new NamedPropertySet(name,
                 new HashMap<String, Object>());

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f5c78e7/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
index e00f60f..f2b82d6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
@@ -17,11 +17,13 @@
  */
 package org.apache.ambari.server.controller.internal;
 
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
@@ -36,11 +38,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.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.orm.dao.AlertDispatchDAO;
 import org.apache.ambari.server.orm.entities.AlertTargetEntity;
 import org.apache.ambari.server.state.alert.AlertTarget;
 import org.apache.commons.lang.StringUtils;
 
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 
@@ -70,6 +75,11 @@ public class AlertTargetResourceProvider extends
   private static AlertDispatchDAO s_dao;
 
   /**
+   * Used for serializationa and deserialization of some fields.
+   */
+  private static final Gson s_gson = new Gson();
+
+  /**
    * Initializes the injectable members of this class with the specified
    * injector.
    *
@@ -143,11 +153,21 @@ public class AlertTargetResourceProvider 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 {
+        updateAlertTargets(request.getProperties());
+        return null;
+      }
+    });
+
+    notifyUpdate(Resource.Type.AlertTarget, request, predicate);
+    return getRequestStatus(null);
   }
 
   @Override
@@ -203,7 +223,6 @@ public class AlertTargetResourceProvider extends
       String name = (String) requestMap.get(ALERT_TARGET_NAME);
       String description = (String) requestMap.get(ALERT_TARGET_DESCRIPTION);
       String notificationType = (String) requestMap.get(ALERT_TARGET_NOTIFICATION_TYPE);
-      String properties = (String) requestMap.get(ALERT_TARGET_PROPERTIES);
 
       if (StringUtils.isEmpty(name)) {
         throw new IllegalArgumentException(
@@ -215,6 +234,12 @@ public class AlertTargetResourceProvider extends
             "The type of the alert target is required.");
       }
 
+      String properties = extractProperties(requestMap);
+      if (StringUtils.isEmpty(properties)) {
+        throw new IllegalArgumentException(
+            "Alert targets must be created with their connection properties");
+      }
+
       entity.setDescription(description);
       entity.setNotificationType(notificationType);
       entity.setProperties(properties);
@@ -227,10 +252,65 @@ public class AlertTargetResourceProvider extends
   }
 
   /**
+   * Updates existing {@link AlertTargetEntity}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.
+   */
+  private void updateAlertTargets(Set<Map<String, Object>> requestMaps)
+      throws AmbariException {
+
+    for (Map<String, Object> requestMap : requestMaps) {
+      String stringId = (String) requestMap.get(ALERT_TARGET_ID);
+
+      if (StringUtils.isEmpty(stringId)) {
+        throw new IllegalArgumentException(
+            "The ID of the alert target is required when updating an existing target");
+      }
+
+      long id = Long.parseLong(stringId);
+      AlertTargetEntity entity = s_dao.findTargetById(id);
+
+      if (null == entity) {
+        String message = MessageFormat.format(
+            "The alert target with ID {0} could not be found", id);
+        throw new AmbariException(message);
+      }
+
+      String name = (String) requestMap.get(ALERT_TARGET_NAME);
+      String description = (String) requestMap.get(ALERT_TARGET_DESCRIPTION);
+      String notificationType = (String) requestMap.get(ALERT_TARGET_NOTIFICATION_TYPE);
+
+      if (!StringUtils.isBlank(name)) {
+        entity.setTargetName(name);
+      }
+
+      if (null != description) {
+        entity.setDescription(description);
+      }
+
+      if (!StringUtils.isBlank(notificationType)) {
+        entity.setNotificationType(notificationType);
+      }
+
+      String properties = extractProperties(requestMap);
+      if (!StringUtils.isEmpty(properties)) {
+        entity.setProperties(properties);
+      }
+
+      s_dao.merge(entity);
+    }
+  }
+
+  /**
    * Convert the given {@link AlertTargetEntity} to a {@link Resource}.
    *
    * @param isCollection
-   *          {@code true} if the resource is part of a collection.
+   *          {@code true} if the resource is part of a collection. Some
+   *          properties are not returned when a collection of targets is
+   *          requested.
    * @param entity
    *          the entity to convert.
    * @param requestedIds
@@ -244,15 +324,49 @@ public class AlertTargetResourceProvider extends
     resource.setProperty(ALERT_TARGET_ID, entity.getTargetId());
     resource.setProperty(ALERT_TARGET_NAME, entity.getTargetName());
 
-    setResourceProperty(resource, ALERT_TARGET_DESCRIPTION,
-        entity.getDescription(), requestedIds);
+    resource.setProperty(ALERT_TARGET_DESCRIPTION, entity.getDescription());
+    resource.setProperty(ALERT_TARGET_NOTIFICATION_TYPE,
+        entity.getNotificationType());
 
-    setResourceProperty(resource, ALERT_TARGET_NOTIFICATION_TYPE,
-        entity.getNotificationType(), requestedIds);
+    if (!isCollection) {
+      String properties = entity.getProperties();
+      Map<String, String> map = s_gson.<Map<String, String>> fromJson(
+          properties,
+          Map.class);
 
-    setResourceProperty(resource, ALERT_TARGET_PROPERTIES,
-        entity.getProperties(), requestedIds);
+      resource.setProperty(ALERT_TARGET_PROPERTIES, map);
+    }
 
     return resource;
   }
+
+  /**
+   * Looks through the flat list of propery keys in the supplied map and builds
+   * a JSON string of key:value pairs for the {@link #ALERT_TARGET_PROPERTIES}
+   * key.
+   *
+   * @param requestMap
+   *          the map of flattened properties (not {@code null}).
+   * @return the JSON representing the key/value pairs of all properties, or
+   *         {@code null} if none.
+   */
+  private String extractProperties( Map<String, Object> requestMap ){
+    JsonObject jsonObject = new JsonObject();
+    for (Entry<String, Object> entry : requestMap.entrySet()) {
+      String key = entry.getKey();
+      String propCat = PropertyHelper.getPropertyCategory(key);
+
+      if (propCat.equals(ALERT_TARGET_PROPERTIES)) {
+        String propKey = PropertyHelper.getPropertyName(key);
+        jsonObject.addProperty(propKey, entry.getValue().toString());
+      }
+    }
+
+    String properties = null;
+    if (jsonObject.entrySet().size() > 0) {
+      properties = jsonObject.toString();
+    }
+
+    return properties;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f5c78e7/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigFactory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigFactory.java
index 28dfc1c..eaf68aa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigFactory.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigFactory.java
@@ -25,8 +25,6 @@ import com.google.inject.assistedinject.Assisted;
 
 /**
  * Factory for creating configuration objects using {@link Assisted} constructor parameters
- *
- * @author ncole
  */
 public interface ConfigFactory {
   

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f5c78e7/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 f495787..2d10dda 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,6 +17,8 @@
  */
 package org.apache.ambari.server.state.alert;
 
+import java.util.Map;
+
 import org.apache.ambari.server.orm.entities.AlertTargetEntity;
 import org.codehaus.jackson.annotate.JsonProperty;
 
@@ -31,7 +33,7 @@ public class AlertTarget {
   private String m_name;
   private String m_description;
   private String m_notificationType;
-  private String m_properties;
+  private Map<String, String> m_properties;
 
   /**
    * @return the id
@@ -101,7 +103,7 @@ public class AlertTarget {
    * @return the properties
    */
   @JsonProperty("properties")
-  public String getProperties() {
+  public Map<String, String> getProperties() {
     return m_properties;
   }
 
@@ -109,7 +111,7 @@ public class AlertTarget {
    * @param properties
    *          the properties to set
    */
-  public void setProperties(String properties) {
+  public void setProperties(Map<String, String> properties) {
     m_properties = properties;
   }
 
@@ -123,8 +125,6 @@ public class AlertTarget {
     target.setDescription(entity.getDescription());
     target.setName(entity.getTargetName());
     target.setNotificationType(entity.getNotificationType());
-    target.setProperties(entity.getProperties());
-
     return target;
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/2f5c78e7/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 6ca2035..1c964a3 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
@@ -17,6 +17,8 @@
  */
 package org.apache.ambari.server.controller.internal;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createStrictMock;
@@ -25,7 +27,6 @@ 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.Arrays;
 import java.util.Collections;
@@ -68,13 +69,17 @@ public class AlertTargetResourceProviderTest {
   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 String ALERT_TARGET_PROPS = "{\"foo\":\"bar\",\"foobar\":\"baz\"}";
+  private static final String ALERT_TARGET_PROPS2 = "{\"foobar\":\"baz\"}";
 
   private AlertDispatchDAO m_dao;
   private Injector m_injector;
+  private AmbariManagementController m_amc;
 
   @Before
   public void before() {
     m_dao = createStrictMock(AlertDispatchDAO.class);
+    m_amc = createMock(AmbariManagementController.class);
 
     m_injector = Guice.createInjector(Modules.override(
         new InMemoryDefaultTestModule()).with(new MockModule()));
@@ -96,8 +101,7 @@ public class AlertTargetResourceProviderTest {
     expect(m_dao.findAllTargets()).andReturn(getMockEntities());
     replay(m_dao);
 
-    AmbariManagementController amc = createMock(AmbariManagementController.class);
-    AlertTargetResourceProvider provider = createProvider(amc);
+    AlertTargetResourceProvider provider = createProvider(m_amc);
     Set<Resource> results = provider.getResources(request, null);
 
     assertEquals(1, results.size());
@@ -113,6 +117,7 @@ public class AlertTargetResourceProviderTest {
    * @throws Exception
    */
   @Test
+  @SuppressWarnings("unchecked")
   public void testGetSingleResource() throws Exception {
     Request request = PropertyHelper.getReadRequest(
         AlertTargetResourceProvider.ALERT_TARGET_DESCRIPTION,
@@ -120,8 +125,6 @@ public class AlertTargetResourceProviderTest {
         AlertTargetResourceProvider.ALERT_TARGET_NAME,
         AlertTargetResourceProvider.ALERT_TARGET_NOTIFICATION_TYPE);
 
-    AmbariManagementController amc = createMock(AmbariManagementController.class);
-
     Predicate predicate = new PredicateBuilder().property(
         AlertTargetResourceProvider.ALERT_TARGET_ID).equals(
         ALERT_TARGET_ID.toString()).toPredicate();
@@ -129,9 +132,9 @@ public class AlertTargetResourceProviderTest {
     expect(m_dao.findTargetById(ALERT_TARGET_ID.longValue())).andReturn(
         getMockEntities().get(0));
 
-    replay(amc, m_dao);
+    replay(m_amc, m_dao);
 
-    AlertTargetResourceProvider provider = createProvider(amc);
+    AlertTargetResourceProvider provider = createProvider(m_amc);
     Set<Resource> results = provider.getResources(request, predicate);
 
     assertEquals(1, results.size());
@@ -143,7 +146,11 @@ public class AlertTargetResourceProviderTest {
     Assert.assertEquals(ALERT_TARGET_NAME,
         r.getPropertyValue(AlertTargetResourceProvider.ALERT_TARGET_NAME));
 
-    verify(amc, m_dao);
+    Map<String, String> properties = (Map<String, String>) r.getPropertyValue(AlertTargetResourceProvider.ALERT_TARGET_PROPERTIES);
+    assertEquals( "bar", properties.get("foo") );
+    assertEquals( "baz", properties.get("foobar") );
+
+    verify(m_amc, m_dao);
   }
 
   /**
@@ -151,25 +158,15 @@ public class AlertTargetResourceProviderTest {
    */
   @Test
   public void testCreateResources() throws Exception {
-    AmbariManagementController amc = createMock(AmbariManagementController.class);
     Capture<List<AlertTargetEntity>> listCapture = new Capture<List<AlertTargetEntity>>();
 
     m_dao.createTargets(capture(listCapture));
     expectLastCall();
 
-    replay(amc, m_dao);
-
-    AlertTargetResourceProvider provider = createProvider(amc);
-    Map<String, Object> requestProps = new HashMap<String, Object>();
-    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_NAME,
-        ALERT_TARGET_NAME);
+    replay(m_amc, m_dao);
 
-    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_DESCRIPTION,
-        ALERT_TARGET_DESC);
-
-    requestProps.put(
-        AlertTargetResourceProvider.ALERT_TARGET_NOTIFICATION_TYPE,
-        ALERT_TARGET_TYPE);
+    AlertTargetResourceProvider provider = createProvider(m_amc);
+    Map<String, Object> requestProps = getCreationProperties();
 
     Request request = PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
     provider.createResources(request);
@@ -178,18 +175,63 @@ public class AlertTargetResourceProviderTest {
     AlertTargetEntity entity = listCapture.getValue().get(0);
     Assert.assertNotNull(entity);
 
-    Assert.assertEquals(ALERT_TARGET_NAME, entity.getTargetName());
-    Assert.assertEquals(ALERT_TARGET_DESC, entity.getDescription());
-    Assert.assertEquals(ALERT_TARGET_TYPE, entity.getNotificationType());
+    assertEquals(ALERT_TARGET_NAME, entity.getTargetName());
+    assertEquals(ALERT_TARGET_DESC, entity.getDescription());
+    assertEquals(ALERT_TARGET_TYPE, entity.getNotificationType());
+    assertEquals(ALERT_TARGET_PROPS, entity.getProperties());
 
-    verify(amc, m_dao);
+    verify(m_amc, m_dao);
   }
 
   /**
    * @throws Exception
    */
   @Test
+  @SuppressWarnings("unchecked")
   public void testUpdateResources() throws Exception {
+    Capture<AlertTargetEntity> entityCapture = new Capture<AlertTargetEntity>();
+
+    m_dao.createTargets(EasyMock.anyObject(List.class));
+    expectLastCall().times(1);
+
+    AlertTargetEntity target = new AlertTargetEntity();
+    expect(m_dao.findTargetById(ALERT_TARGET_ID)).andReturn(target).times(1);
+
+    expect(m_dao.merge(capture(entityCapture))).andReturn(target).once();
+
+    replay(m_amc, m_dao);
+
+    AlertTargetResourceProvider provider = createProvider(m_amc);
+    Map<String, Object> requestProps = getCreationProperties();
+    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(AlertTargetResourceProvider.ALERT_TARGET_ID,
+        ALERT_TARGET_ID.toString());
+
+    String newName = ALERT_TARGET_NAME + " Foo";
+    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_NAME, newName);
+
+    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_PROPERTIES
+        + "/foobar", "baz");
+
+    Predicate predicate = new PredicateBuilder().property(
+        AlertTargetResourceProvider.ALERT_TARGET_ID).equals(
+        ALERT_TARGET_ID.toString()).toPredicate();
+
+    request = PropertyHelper.getUpdateRequest(requestProps, null);
+    provider.updateResources(request, predicate);
+
+    assertTrue(entityCapture.hasCaptured());
+
+    AlertTargetEntity entity = entityCapture.getValue();
+    assertEquals(newName, entity.getTargetName());
+    assertEquals(ALERT_TARGET_PROPS2, entity.getProperties());
+    verify(m_amc, m_dao);
   }
 
   /**
@@ -197,28 +239,17 @@ public class AlertTargetResourceProviderTest {
    */
   @Test
   public void testDeleteResources() throws Exception {
-    AmbariManagementController amc = createMock(AmbariManagementController.class);
     Capture<AlertTargetEntity> entityCapture = new Capture<AlertTargetEntity>();
     Capture<List<AlertTargetEntity>> listCapture = new Capture<List<AlertTargetEntity>>();
 
     m_dao.createTargets(capture(listCapture));
     expectLastCall();
 
-    replay(amc, m_dao);
+    replay(m_amc, m_dao);
 
-    AlertTargetResourceProvider provider = createProvider(amc);
-
-    Map<String, Object> requestProps = new HashMap<String, Object>();
-    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_NAME,
-        ALERT_TARGET_NAME);
-
-    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_DESCRIPTION,
-        ALERT_TARGET_DESC);
-
-    requestProps.put(
-        AlertTargetResourceProvider.ALERT_TARGET_NOTIFICATION_TYPE,
-        ALERT_TARGET_TYPE);
+    AlertTargetResourceProvider provider = createProvider(m_amc);
 
+    Map<String, Object> requestProps = getCreationProperties();
     Request request = PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
     provider.createResources(request);
 
@@ -244,7 +275,7 @@ public class AlertTargetResourceProviderTest {
     AlertTargetEntity entity1 = entityCapture.getValue();
     Assert.assertEquals(ALERT_TARGET_ID, entity1.getTargetId());
 
-    verify(amc, m_dao);
+    verify(m_amc, m_dao);
   }
 
   /**
@@ -267,10 +298,39 @@ public class AlertTargetResourceProviderTest {
     entity.setDescription(ALERT_TARGET_DESC);
     entity.setTargetName(ALERT_TARGET_NAME);
     entity.setNotificationType(ALERT_TARGET_TYPE);
+    entity.setProperties(ALERT_TARGET_PROPS);
     return Arrays.asList(entity);
   }
 
   /**
+   * Gets the maps of properties that simulate a deserialzied JSON request to
+   * create an alert target.
+   *
+   * @return
+   * @throws Exception
+   */
+  private Map<String, Object> getCreationProperties() throws Exception {
+    Map<String, Object> requestProps = new HashMap<String, Object>();
+    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_NAME,
+        ALERT_TARGET_NAME);
+
+    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_DESCRIPTION,
+        ALERT_TARGET_DESC);
+
+    requestProps.put(
+        AlertTargetResourceProvider.ALERT_TARGET_NOTIFICATION_TYPE,
+        ALERT_TARGET_TYPE);
+
+    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_PROPERTIES
+        + "/foo", "bar");
+
+    requestProps.put(AlertTargetResourceProvider.ALERT_TARGET_PROPERTIES
+        + "/foobar", "baz");
+
+    return requestProps;
+  }
+
+  /**
   *
   */
   private class MockModule implements Module {