You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by js...@apache.org on 2015/01/30 17:54:57 UTC

ambari git commit: AMBARI-9410. Allow cluster and service artifacts to be updated and deleted

Repository: ambari
Updated Branches:
  refs/heads/trunk b2f392d8f -> ee4e786a4


AMBARI-9410. Allow cluster and service artifacts to be updated and deleted


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

Branch: refs/heads/trunk
Commit: ee4e786a4daf639da34ea81a7ff1a11f3dad378f
Parents: b2f392d
Author: John Speidel <js...@hortonworks.com>
Authored: Thu Jan 29 17:27:14 2015 -0500
Committer: John Speidel <js...@hortonworks.com>
Committed: Fri Jan 30 11:54:39 2015 -0500

----------------------------------------------------------------------
 .../server/api/services/ClusterService.java     | 124 ++++++++--
 .../server/api/services/ServiceService.java     |  92 ++++++++
 .../internal/ArtifactResourceProvider.java      |  84 ++++++-
 .../ambari/server/orm/dao/ArtifactDAO.java      |  22 +-
 .../server/api/services/ClusterServiceTest.java |  50 +++-
 .../server/api/services/ServiceServiceTest.java |  59 +++++
 .../internal/ArtifactResourceProviderTest.java  | 235 ++++++++++++++++---
 7 files changed, 607 insertions(+), 59 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/ee4e786a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
index 5f44b2f..a794693 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
@@ -185,10 +185,10 @@ public class ClusterService extends BaseService {
   @GET
   @Path("{clusterName}/artifacts")
   @Produces("text/plain")
-  public Response getClusterArtifacts(String body,
-                                      @Context HttpHeaders headers,
-                                      @Context UriInfo ui,
-                                      @PathParam("clusterName") String clusterName) {
+  public Response getArtifacts(String body,
+                               @Context HttpHeaders headers,
+                               @Context UriInfo ui,
+                               @PathParam("clusterName") String clusterName) {
 
     hasPermission(Request.Type.GET, clusterName);
     return handleRequest(headers, body, ui, Request.Type.GET,
@@ -210,11 +210,11 @@ public class ClusterService extends BaseService {
   @GET
   @Path("{clusterName}/artifacts/{artifactName}")
   @Produces("text/plain")
-  public Response getClusterArtifact(String body,
-                                      @Context HttpHeaders headers,
-                                      @Context UriInfo ui,
-                                      @PathParam("clusterName") String clusterName,
-                                      @PathParam("artifactName") String artifactName) {
+  public Response getArtifact(String body,
+                              @Context HttpHeaders headers,
+                              @Context UriInfo ui,
+                              @PathParam("clusterName") String clusterName,
+                              @PathParam("artifactName") String artifactName) {
 
     hasPermission(Request.Type.GET, clusterName);
     return handleRequest(headers, body, ui, Request.Type.GET,
@@ -235,11 +235,11 @@ public class ClusterService extends BaseService {
   @POST
   @Path("{clusterName}/artifacts/{artifactName}")
   @Produces("text/plain")
-  public Response createClusterArtifact(String body,
-                                      @Context HttpHeaders headers,
-                                      @Context UriInfo ui,
-                                      @PathParam("clusterName") String clusterName,
-                                      @PathParam("artifactName") String artifactName) {
+  public Response createArtifact(String body,
+                                 @Context HttpHeaders headers,
+                                 @Context UriInfo ui,
+                                 @PathParam("clusterName") String clusterName,
+                                 @PathParam("artifactName") String artifactName) {
 
     hasPermission(Request.Type.POST, clusterName);
     return handleRequest(headers, body, ui, Request.Type.POST,
@@ -247,6 +247,102 @@ public class ClusterService extends BaseService {
   }
 
   /**
+   * Handles: PUT /clusters/{clusterID}/artifacts
+   * Update all artifacts matching the provided predicate.
+   *
+   * @param headers      http headers
+   * @param ui           uri info
+   * @param clusterName  cluster name
+   *
+   * @return information regarding the updated artifacts
+   */
+  @PUT
+  @Path("{clusterName}/artifacts")
+  @Produces("text/plain")
+  public Response updateArtifacts(String body,
+                                  @Context HttpHeaders headers,
+                                  @Context UriInfo ui,
+                                  @PathParam("clusterName") String clusterName) {
+
+    hasPermission(Request.Type.PUT, clusterName);
+    return handleRequest(headers, body, ui, Request.Type.PUT,
+        createArtifactResource(clusterName, null));
+  }
+
+  /**
+   * Handles: PUT /clusters/{clusterID}/artifacts/{artifactName}
+   * Update a specific artifact.
+   *
+   * @param headers       http headers
+   * @param ui            uri info
+   * @param clusterName   cluster name
+   * @param artifactName  artifactName
+   *
+   * @return information regarding the updated artifact
+   */
+  @PUT
+  @Path("{clusterName}/artifacts/{artifactName}")
+  @Produces("text/plain")
+  public Response updateArtifact(String body,
+                                 @Context HttpHeaders headers,
+                                @Context UriInfo ui,
+                                @PathParam("clusterName") String clusterName,
+                                @PathParam("artifactName") String artifactName) {
+
+    hasPermission(Request.Type.PUT, clusterName);
+    return handleRequest(headers, body, ui, Request.Type.PUT,
+        createArtifactResource(clusterName, artifactName));
+  }
+
+  /**
+   * Handles: DELETE /clusters/{clusterID}/artifacts/{artifactName}
+   * Delete a specific artifact.
+   *
+   * @param headers       http headers
+   * @param ui            uri info
+   * @param clusterName   cluster name
+   * @param artifactName  artifactName
+   *
+   * @return information regarding the deleted artifact
+   */
+  @DELETE
+  @Path("{clusterName}/artifacts/{artifactName}")
+  @Produces("text/plain")
+  public Response deleteArtifact(String body,
+                                 @Context HttpHeaders headers,
+                                 @Context UriInfo ui,
+                                 @PathParam("clusterName") String clusterName,
+                                 @PathParam("artifactName") String artifactName) {
+
+    hasPermission(Request.Type.DELETE, clusterName);
+    return handleRequest(headers, body, ui, Request.Type.DELETE,
+        createArtifactResource(clusterName, artifactName));
+  }
+
+  /**
+   * Handles: DELETE /clusters/{clusterID}/artifacts
+   * Delete all artifacts matching the provided predicate.
+   *
+   * @param headers      http headers
+   * @param ui           uri info
+   * @param clusterName  cluster name
+   *
+   * @return information regarding the deleted artifacts
+   */
+  @DELETE
+  @Path("{clusterName}/artifacts")
+  @Produces("text/plain")
+  public Response deleteArtifacts(String body,
+                                  @Context HttpHeaders headers,
+                                  @Context UriInfo ui,
+                                  @PathParam("clusterName") String clusterName) {
+
+    hasPermission(Request.Type.DELETE, clusterName);
+    return handleRequest(headers, body, ui, Request.Type.DELETE,
+        createArtifactResource(clusterName, null));
+  }
+
+  /**
    * Get the hosts sub-resource
    *
    * @param request      the request

http://git-wip-us.apache.org/repos/asf/ambari/blob/ee4e786a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
index 8a2642b..ad6d68c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
@@ -273,6 +273,98 @@ public class ServiceService extends BaseService {
   }
 
   /**
+   * Handles: PUT /clusters/{clusterID}/services/{serviceName}/artifacts
+   * Update all artifacts matching the provided predicate.
+   *
+   * @param headers      http headers
+   * @param ui           uri info
+   * @param serviceName  service name
+   *
+   * @return information regarding the updated artifacts
+   */
+  @PUT
+  @Path("{serviceName}/artifacts")
+  @Produces("text/plain")
+  public Response updateArtifacts(String body,
+                                  @Context HttpHeaders headers,
+                                  @Context UriInfo ui,
+                                  @PathParam("serviceName") String serviceName) {
+
+    return handleRequest(headers, body, ui, Request.Type.PUT,
+        createArtifactResource(m_clusterName, serviceName, null));
+  }
+
+  /**
+   * Handles: PUT /clusters/{clusterID}/services/{serviceName}/artifacts/{artifactName}
+   * Update a specific artifact.
+   *
+   * @param headers       http headers
+   * @param ui            uri info
+   * @param serviceName   service name
+   * @param artifactName  artifact name
+   *
+   * @return information regarding the updated artifact
+   */
+  @PUT
+  @Path("{serviceName}/artifacts/{artifactName}")
+  @Produces("text/plain")
+  public Response updateArtifact(String body,
+                                 @Context HttpHeaders headers,
+                                 @Context UriInfo ui,
+                                 @PathParam("serviceName") String serviceName,
+                                 @PathParam("artifactName") String artifactName) {
+
+    return handleRequest(headers, body, ui, Request.Type.PUT,
+        createArtifactResource(m_clusterName, serviceName, artifactName));
+  }
+
+  /**
+   * Handles: DELETE /clusters/{clusterID}/services/{serviceName}/artifacts
+   * Delete all artifacts matching the provided predicate.
+   *
+   * @param headers      http headers
+   * @param ui           uri info
+   * @param serviceName  service name
+   *
+   * @return information regarding the deleted artifacts
+   */
+  @DELETE
+  @Path("{serviceName}/artifacts")
+  @Produces("text/plain")
+  public Response deleteArtifacts(String body,
+                                  @Context HttpHeaders headers,
+                                  @Context UriInfo ui,
+                                  @PathParam("serviceName") String serviceName) {
+
+    return handleRequest(headers, body, ui, Request.Type.DELETE,
+        createArtifactResource(m_clusterName, serviceName, null));
+  }
+
+  /**
+   * Handles: DELETE /clusters/{clusterID}/services/{serviceName}/artifacts/{artifactName}
+   * Delete a specific artifact.
+   *
+   * @param headers       http headers
+   * @param ui            uri info
+   * @param serviceName   service name
+   * @param artifactName  artifact name
+   *
+   * @return information regarding the deleted artifact
+   */
+  @DELETE
+  @Path("{serviceName}/artifacts/{artifactName}")
+  @Produces("text/plain")
+  public Response deleteArtifact(String body,
+                                 @Context HttpHeaders headers,
+                                 @Context UriInfo ui,
+                                 @PathParam("serviceName") String serviceName,
+                                 @PathParam("artifactName") String artifactName) {
+
+    return handleRequest(headers, body, ui, Request.Type.DELETE,
+        createArtifactResource(m_clusterName, serviceName, artifactName));
+  }
+
+  /**
    * Gets the alert history service
    *
    * @param request

http://git-wip-us.apache.org/repos/asf/ambari/blob/ee4e786a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java
index e476f62..680f9b8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java
@@ -216,13 +216,18 @@ public class ArtifactResourceProvider extends AbstractResourceProvider {
   }
 
   @Override
-  public RequestStatus updateResources(Request request, Predicate predicate)
+  public RequestStatus updateResources(final Request request, Predicate predicate)
       throws SystemException,
              UnsupportedPropertyException,
              NoSuchResourceException,
              NoSuchParentResourceException {
 
-    throw new UnsupportedOperationException("Update not currently supported for Artifact resources");
+    for (Resource resource : getResources(request, predicate)) {
+      modifyResources(getUpdateCommand(request, resource));
+    }
+
+    notifyUpdate(Resource.Type.Artifact, request, predicate);
+    return getRequestStatus(null);
   }
 
   @Override
@@ -232,11 +237,20 @@ public class ArtifactResourceProvider extends AbstractResourceProvider {
              NoSuchResourceException,
              NoSuchParentResourceException {
 
-    throw new UnsupportedOperationException("Delete not currently supported for Artifact resources");
+    // get resources to update
+    Set<Resource> setResources = getResources(
+        new RequestImpl(null, null, null, null), predicate);
+
+    for (final Resource resource : setResources) {
+      modifyResources(getDeleteCommand(resource));
+    }
+
+    notifyDelete(Resource.Type.Artifact, predicate);
+    return getRequestStatus(null);
   }
 
   /**
-   * Create a command to create the resource.
+   * Create a command to create a resource.
    *
    * @param properties        request properties
    * @param requestInfoProps  request info properties
@@ -314,6 +328,65 @@ public class ArtifactResourceProvider extends AbstractResourceProvider {
   }
 
   /**
+   * Create a command to update a resource.
+   *
+   * @param request   update request
+   * @param resource  resource to update
+   *
+   * @return  a update resource command
+   */
+  private Command<Void> getUpdateCommand(final Request request, final Resource resource) {
+    return new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        Map<String, Object> entityUpdateProperties =
+            new HashMap<String, Object>(request.getProperties().iterator().next());
+
+        // ensure name is set.  It won't be in case of query
+        entityUpdateProperties.put(ARTIFACT_NAME_PROPERTY,
+            String.valueOf(resource.getPropertyValue(ARTIFACT_NAME_PROPERTY)));
+
+        artifactDAO.merge(toEntity(entityUpdateProperties,
+            request.getRequestInfoProperties().get(Request.REQUEST_INFO_BODY_PROPERTY)));
+
+        return null;
+      }
+    };
+  }
+
+  /**
+   * Create a command to delete a resource.
+   *
+   * @param resource the resource to delete
+   *
+   * @return  a delete resource command
+   */
+  private Command<Void> getDeleteCommand(final Resource resource) {
+    return new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        Map<String, Object> keyProperties = new HashMap<String, Object>();
+
+        // flatten out key properties as is expected by createForeignKeyMap()
+        for (Map.Entry<String, Object> entry : resource.getPropertiesMap().get("Artifacts").entrySet()) {
+          keyProperties.put(String.format("Artifacts/%s", entry.getKey()), entry.getValue());
+        }
+
+        // create entity and set properties
+        final ArtifactEntity entity = new ArtifactEntity();
+        entity.setArtifactName(String.valueOf(resource.getPropertyValue(ARTIFACT_NAME_PROPERTY)));
+        entity.setForeignKeys(createForeignKeyMap(keyProperties));
+
+        LOG.info("Deleting Artifact, name = {}, foreign keys = {}",
+            entity.getArtifactName(), entity.getForeignKeys());
+
+        artifactDAO.remove(entity);
+        return null;
+      }
+    };
+  }
+
+  /**
    * Validate that parent resources exist.
    *
    * @param properties  request properties
@@ -394,6 +467,9 @@ public class ArtifactResourceProvider extends AbstractResourceProvider {
         rawRequestBody, Map.class);
 
     Object artifactData = rawBodyMap.get(ARTIFACT_DATA_PROPERTY);
+    if (artifactData == null) {
+      throw new IllegalArgumentException("artifact_data property must be provided");
+    }
     if (! (artifactData instanceof Map)) {
       throw new IllegalArgumentException("artifact_data property must be a map");
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ee4e786a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java
index 4b7cf25..27346dd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java
@@ -30,7 +30,6 @@ import javax.persistence.EntityManager;
 import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
 import java.util.List;
-import java.util.Map;
 import java.util.TreeMap;
 
 
@@ -106,4 +105,25 @@ public class ArtifactDAO {
   public void create(ArtifactEntity entity) {
     entityManagerProvider.get().persist(entity);
   }
+
+  /**
+   * Merge the state of the given entity into the current persistence context.
+   *
+   * @param artifactEntity  entity to merge
+   * @return the merged entity
+   */
+  @Transactional
+  public ArtifactEntity merge(ArtifactEntity artifactEntity) {
+    return entityManagerProvider.get().merge(artifactEntity);
+  }
+
+  /**
+   * Remove the entity instance.
+   *
+   * @param artifactEntity  entity to remove
+   */
+  @Transactional
+  public void remove(ArtifactEntity artifactEntity) {
+    entityManagerProvider.get().remove(merge(artifactEntity));
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ee4e786a/ambari-server/src/test/java/org/apache/ambari/server/api/services/ClusterServiceTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/ClusterServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/ClusterServiceTest.java
index c8f020e..c36f5fe 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/ClusterServiceTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/ClusterServiceTest.java
@@ -66,7 +66,7 @@ public class ClusterServiceTest extends BaseServiceTest {
     args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName"};
     listInvocations.add(new ServiceTestInvocation(Request.Type.POST, clusterService, m, args, "body"));
 
-    //createCluster
+    //updateCluster
     clusterService = new TestClusterService(clusters, "clusterName");
     m = clusterService.getClass().getMethod("updateCluster", String.class, HttpHeaders.class, UriInfo.class, String.class);
     args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName"};
@@ -78,6 +78,48 @@ public class ClusterServiceTest extends BaseServiceTest {
     args = new Object[] {getHttpHeaders(), getUriInfo(), "clusterName"};
     listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, clusterService, m, args, null));
 
+    //createArtifact
+    clusterService = new TestClusterService(clusters, "clusterName");
+    m = clusterService.getClass().getMethod("createArtifact", String.class, HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName", "artifactName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, clusterService, m, args, "body"));
+
+    //getArtifact
+    clusterService = new TestClusterService(clusters, "clusterName");
+    m = clusterService.getClass().getMethod("getArtifact", String.class, HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName", "artifact_name"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, clusterService, m, args, "body"));
+
+    //getArtifacts
+    clusterService = new TestClusterService(clusters, "clusterName");
+    m = clusterService.getClass().getMethod("getArtifacts", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, clusterService, m, args, "body"));
+
+    //updateArtifact
+    clusterService = new TestClusterService(clusters, "clusterName");
+    m = clusterService.getClass().getMethod("updateArtifact", String.class, HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName", "artifactName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, clusterService, m, args, "body"));
+
+    //updateArtifacts
+    clusterService = new TestClusterService(clusters, "clusterName");
+    m = clusterService.getClass().getMethod("updateArtifacts", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, clusterService, m, args, "body"));
+
+    //deleteArtifact
+    clusterService = new TestClusterService(clusters, "clusterName");
+    m = clusterService.getClass().getMethod("deleteArtifact", String.class, HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName", "artifactName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, clusterService, m, args, "body"));
+
+    //deleteArtifacts
+    clusterService = new TestClusterService(clusters, "clusterName");
+    m = clusterService.getClass().getMethod("deleteArtifacts", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, clusterService, m, args, "body"));
+
     return listInvocations;
   }
 
@@ -97,6 +139,12 @@ public class ClusterServiceTest extends BaseServiceTest {
     }
 
     @Override
+    ResourceInstance createArtifactResource(String clusterName, String artifactName) {
+      assertEquals(m_clusterId, clusterName);
+      return getTestResource();
+    }
+
+    @Override
     RequestFactory getRequestFactory() {
       return getTestRequestFactory();
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ee4e786a/ambari-server/src/test/java/org/apache/ambari/server/api/services/ServiceServiceTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/ServiceServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/ServiceServiceTest.java
index 5ca3fb3..5c3e3a7 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/ServiceServiceTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/ServiceServiceTest.java
@@ -81,12 +81,56 @@ public class ServiceServiceTest extends BaseServiceTest {
     args = new Object[] {getHttpHeaders(), getUriInfo(), "serviceName"};
     listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, service, m, args, null));
 
+    //createArtifact
+    service = new TestServiceService("clusterName", "serviceName", "artifactName");
+    m = service.getClass().getMethod("createArtifact", String.class, HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "serviceName", "artifactName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, service, m, args, "body"));
+
+    //getArtifact
+    service = new TestServiceService("clusterName", "serviceName", "artifactName");
+    m = service.getClass().getMethod("getArtifact", String.class, HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "serviceName", "artifactName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, "body"));
+
+    //getArtifacts
+    service = new TestServiceService("clusterName", "serviceName");
+    m = service.getClass().getMethod("getArtifacts", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "serviceName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, "body"));
+
+    //updateArtifact
+    service = new TestServiceService("clusterName", "serviceName", "artifactName");
+    m = service.getClass().getMethod("updateArtifact", String.class, HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "serviceName", "artifactName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, service, m, args, "body"));
+
+    //updateArtifacts
+    service = new TestServiceService("clusterName", "serviceName");
+    m = service.getClass().getMethod("updateArtifacts", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "serviceName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, service, m, args, "body"));
+
+    //deleteArtifact
+    service = new TestServiceService("clusterName", "serviceName", "artifactName");
+    m = service.getClass().getMethod("deleteArtifact", String.class, HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "serviceName", "artifactName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, service, m, args, "body"));
+
+    //deleteArtifacts
+    service = new TestServiceService("clusterName", "serviceName");
+    m = service.getClass().getMethod("deleteArtifacts", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "serviceName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, service, m, args, "body"));
+
+
     return listInvocations;
   }
 
   private class TestServiceService extends ServiceService {
     private String m_clusterId;
     private String m_serviceId;
+    private String m_artifact_id;
 
     private TestServiceService(String clusterId, String serviceId) {
       super(clusterId);
@@ -94,6 +138,13 @@ public class ServiceServiceTest extends BaseServiceTest {
       m_serviceId = serviceId;
     }
 
+    private TestServiceService(String clusterId, String serviceId, String artifactId) {
+      super(clusterId);
+      m_clusterId = clusterId;
+      m_serviceId = serviceId;
+      m_artifact_id = artifactId;
+    }
+
     @Override
     ResourceInstance createServiceResource(String clusterName, String serviceName) {
       assertEquals(m_clusterId, clusterName);
@@ -102,6 +153,14 @@ public class ServiceServiceTest extends BaseServiceTest {
     }
 
     @Override
+    ResourceInstance createArtifactResource(String clusterName, String serviceName, String artifactName) {
+      assertEquals(m_clusterId, clusterName);
+      assertEquals(m_serviceId, serviceName);
+      assertEquals(m_artifact_id, artifactName);
+      return getTestResource();
+    }
+
+    @Override
     RequestFactory getRequestFactory() {
       return getTestRequestFactory();
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ee4e786a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java
index 046c5d8..789fb54 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java
@@ -18,9 +18,11 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import com.google.gson.Gson;
 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.RequestStatus;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.orm.dao.ArtifactDAO;
@@ -51,6 +53,7 @@ import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -58,14 +61,15 @@ import static org.junit.Assert.fail;
 /**
  * ArtifactResourceProvider unit tests.
  */
+@SuppressWarnings("unchecked")
 public class ArtifactResourceProviderTest {
 
   private ArtifactDAO dao = createStrictMock(ArtifactDAO.class);
   private EntityManager em = createStrictMock(EntityManager.class);
   private AmbariManagementController controller = createStrictMock(AmbariManagementController.class);
   private Request request = createMock(Request.class);
-  private Clusters clusters = createStrictMock(Clusters.class);
-  private Cluster cluster = createStrictMock(Cluster.class);
+  private Clusters clusters = createMock(Clusters.class);
+  private Cluster cluster = createMock(Cluster.class);
   private ArtifactEntity entity = createMock(ArtifactEntity.class);
   private ArtifactEntity entity2 = createMock(ArtifactEntity.class);
 
@@ -231,43 +235,6 @@ public class ArtifactResourceProviderTest {
     TreeMap<String, String> foreignKeys = new TreeMap<String, String>();
     foreignKeys.put("cluster", "500");
 
-
-    String bodyJson =
-        "{ " +
-        "  \"artifact_data\" : {" +
-        "    \"foo\" : \"bar\"," +
-        "    \"child\" : {" +
-        "      \"childKey\" : \"childValue\"," +
-        "      \"child2\" : {" +
-        "        \"child2Key\" : \"child2Value\"," +
-        "        \"child3\" : {" +
-        "          \"child4\" : {" +
-        "            \"child4Key\" : \"child4Value\"" +
-        "          }" +
-        "        }" +
-        "      }" +
-        "    }," +
-        "    \"collection\" : [" +
-        "      {" +
-        "        \"child\" : {" +
-        "          \"childKey\" : \"childValue\"," +
-        "          \"child2\" : {" +
-        "            \"child2Key\" : \"child2Value\"," +
-        "            \"child3\" : {" +
-        "              \"child4\" : {" +
-        "                \"child4Key\" : \"child4Value\"" +
-        "              }" +
-        "            }" +
-        "          }" +
-        "        }" +
-        "      }," +
-        "      {" +
-        "        \"child4Key\" : \"child4Value\"" +
-        "      } " +
-        "    ]" +
-        "  }" +
-        "}";
-
     Map<String, String> requestInfoProps = new HashMap<String, String>();
     requestInfoProps.put(Request.REQUEST_INFO_BODY_PROPERTY, bodyJson);
 
@@ -329,6 +296,160 @@ public class ArtifactResourceProviderTest {
     assertEquals(foreignKeys, createEntity.getForeignKeys());
   }
 
+  @Test
+  public void testUpdateResources() throws Exception {
+    Map<String, String> requestInfoProps = new HashMap<String, String>();
+    requestInfoProps.put(Request.REQUEST_INFO_BODY_PROPERTY, bodyJson);
+
+    Capture<ArtifactEntity> updateEntityCapture = new Capture<ArtifactEntity>();
+    Capture<ArtifactEntity> updateEntityCapture2 = new Capture<ArtifactEntity>();
+    Set<String> propertyIds = new HashSet<String>();
+    TreeMap<String, String> foreignKeys = new TreeMap<String, String>();
+    foreignKeys.put("cluster", "500");
+
+    List<ArtifactEntity> entities = new ArrayList<ArtifactEntity>();
+    entities.add(entity);
+    entities.add(entity2);
+
+    Map<String, Object> artifact_data = Collections.<String, Object>singletonMap("foo", "bar");
+    Map<String, Object> artifact_data2 = Collections.<String, Object>singletonMap("foo2", "bar2");
+
+    Map<String, String> responseForeignKeys = new HashMap<String, String>();
+    responseForeignKeys.put("cluster", "500");
+
+    // map with flattened properties
+    Map<String, Object> properties = new HashMap<String, Object>();
+    properties.put("Artifacts/artifact_name", "test-artifact");
+    properties.put("Artifacts/cluster_name", "test-cluster");
+    properties.put("artifact_data/foo", "bar");
+    properties.put("artifact_data/child/childKey", "childValue");
+    properties.put("artifact_data/child/child2/child2Key", "child2Value");
+    properties.put("artifact_data/child/child2/child3/child4/child4Key", "child4Value");
+
+    Set<Map<String, Object>> requestProperties = Collections.singleton(properties);
+
+    Collection<Object> collectionProperties = new HashSet<Object>();
+    properties.put("artifact_data/collection", collectionProperties);
+
+    // expectations
+    expect(request.getProperties()).andReturn(requestProperties).anyTimes();
+    expect(request.getRequestInfoProperties()).andReturn(requestInfoProps).anyTimes();
+    expect(controller.getClusters()).andReturn(clusters).anyTimes();
+    expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes();
+    expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes();
+    expect(cluster.getClusterId()).andReturn(500L).anyTimes();
+    expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes();
+
+    expect(request.getPropertyIds()).andReturn(propertyIds).anyTimes();
+
+    expect(dao.findByForeignKeys(eq(foreignKeys))).andReturn(entities).anyTimes();
+    expect(entity.getArtifactName()).andReturn("test-artifact").anyTimes();
+    expect(entity.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
+    expect(entity.getArtifactData()).andReturn(artifact_data).anyTimes();
+    expect(entity2.getArtifactName()).andReturn("test-artifact2").anyTimes();
+    expect(entity2.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
+    expect(entity2.getArtifactData()).andReturn(artifact_data2).anyTimes();
+
+    expect(dao.merge(capture(updateEntityCapture))).andReturn(entity).once();
+    expect(dao.merge(capture(updateEntityCapture2))).andReturn(entity2).once();
+
+    // end of expectation setting
+    replay(dao, em, controller, request, clusters, cluster, entity, entity2);
+
+    PredicateBuilder pb = new PredicateBuilder();
+    Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").end().toPredicate();
+
+    RequestStatus response = resourceProvider.updateResources(request, predicate);
+    ArtifactEntity updateEntity = updateEntityCapture.getValue();
+    ArtifactEntity updateEntity2 = updateEntityCapture2.getValue();
+
+    Gson serializer = new Gson();
+    ArtifactEntity expected = new ArtifactEntity();
+    expected.setArtifactData((Map<String, Object>) serializer.<Map<String, Object>>fromJson(
+        bodyJson, Map.class).get("artifact_data"));
+
+    assertEquals(expected.getArtifactData(), updateEntity.getArtifactData());
+    assertEquals(expected.getArtifactData(), updateEntity2.getArtifactData());
+
+    if (updateEntity.getArtifactName().equals("test-artifact")) {
+      assertEquals("test-artifact2",updateEntity2.getArtifactName() );
+    } else if (updateEntity.getArtifactName().equals("test-artifact2")) {
+      assertEquals("test-artifact", updateEntity2.getArtifactName());
+    } else {
+      fail ("Unexpected artifact name: " + updateEntity.getArtifactName());
+    }
+
+    assertEquals(foreignKeys, updateEntity.getForeignKeys());
+    assertEquals(foreignKeys, updateEntity2.getForeignKeys());
+
+    assertEquals(RequestStatus.Status.Complete, response.getStatus());
+
+    verify(dao, em, controller, request, clusters, cluster, entity, entity2);
+  }
+
+  @Test
+  public void testDeleteResources() throws Exception {
+
+    Capture<ArtifactEntity> deleteEntityCapture = new Capture<ArtifactEntity>();
+    Capture<ArtifactEntity> deleteEntityCapture2 = new Capture<ArtifactEntity>();
+    TreeMap<String, String> foreignKeys = new TreeMap<String, String>();
+    foreignKeys.put("cluster", "500");
+
+    List<ArtifactEntity> entities = new ArrayList<ArtifactEntity>();
+    entities.add(entity);
+    entities.add(entity2);
+
+    Map<String, Object> artifact_data = Collections.<String, Object>singletonMap("foo", "bar");
+    Map<String, Object> artifact_data2 = Collections.<String, Object>singletonMap("foo2", "bar2");
+
+    Map<String, String> responseForeignKeys = new HashMap<String, String>();
+    responseForeignKeys.put("cluster", "500");
+
+    // expectations
+    expect(controller.getClusters()).andReturn(clusters).anyTimes();
+    expect(clusters.getCluster("test-cluster")).andReturn(cluster).anyTimes();
+    expect(clusters.getClusterById(500L)).andReturn(cluster).anyTimes();
+    expect(cluster.getClusterId()).andReturn(500L).anyTimes();
+    expect(cluster.getClusterName()).andReturn("test-cluster").anyTimes();
+
+
+    expect(dao.findByForeignKeys(eq(foreignKeys))).andReturn(entities).anyTimes();
+    expect(entity.getArtifactName()).andReturn("test-artifact").anyTimes();
+    expect(entity.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
+    expect(entity.getArtifactData()).andReturn(artifact_data).anyTimes();
+    expect(entity2.getArtifactName()).andReturn("test-artifact2").anyTimes();
+    expect(entity2.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
+    expect(entity2.getArtifactData()).andReturn(artifact_data2).anyTimes();
+
+    dao.remove(capture(deleteEntityCapture));
+    dao.remove(capture(deleteEntityCapture2));
+
+    // end of expectation setting
+    replay(dao, em, controller, request, clusters, cluster, entity, entity2);
+
+    PredicateBuilder pb = new PredicateBuilder();
+    Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").end().toPredicate();
+
+    RequestStatus response = resourceProvider.deleteResources(predicate);
+    ArtifactEntity deleteEntity = deleteEntityCapture.getValue();
+    ArtifactEntity deleteEntity2 = deleteEntityCapture2.getValue();
+
+    if (deleteEntity.getArtifactName().equals("test-artifact")) {
+      assertEquals("test-artifact2", deleteEntity2.getArtifactName());
+    } else if (deleteEntity.getArtifactName().equals("test-artifact2")) {
+      assertEquals("test-artifact", deleteEntity2.getArtifactName());
+    } else {
+      fail ("Unexpected artifact name: " + deleteEntity.getArtifactName());
+    }
+
+    assertEquals(foreignKeys, deleteEntity.getForeignKeys());
+    assertEquals(foreignKeys, deleteEntity2.getForeignKeys());
+
+    assertEquals(RequestStatus.Status.Complete, response.getStatus());
+
+    verify(dao, em, controller, request, clusters, cluster, entity, entity2);
+  }
+
 
   private void setPrivateField(Object o, String field, Object value) throws Exception{
     Class<?> c = o.getClass();
@@ -336,4 +457,40 @@ public class ArtifactResourceProviderTest {
     f.setAccessible(true);
     f.set(o, value);
   }
+
+  private String bodyJson =
+      "{ " +
+          "  \"artifact_data\" : {" +
+          "    \"foo\" : \"bar\"," +
+          "    \"child\" : {" +
+          "      \"childKey\" : \"childValue\"," +
+          "      \"child2\" : {" +
+          "        \"child2Key\" : \"child2Value\"," +
+          "        \"child3\" : {" +
+          "          \"child4\" : {" +
+          "            \"child4Key\" : \"child4Value\"" +
+          "          }" +
+          "        }" +
+          "      }" +
+          "    }," +
+          "    \"collection\" : [" +
+          "      {" +
+          "        \"child\" : {" +
+          "          \"childKey\" : \"childValue\"," +
+          "          \"child2\" : {" +
+          "            \"child2Key\" : \"child2Value\"," +
+          "            \"child3\" : {" +
+          "              \"child4\" : {" +
+          "                \"child4Key\" : \"child4Value\"" +
+          "              }" +
+          "            }" +
+          "          }" +
+          "        }" +
+          "      }," +
+          "      {" +
+          "        \"child4Key\" : \"child4Value\"" +
+          "      } " +
+          "    ]" +
+          "  }" +
+          "}";
 }