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/15 19:27:53 UTC
ambari git commit: AMBARI-9142. Create new API endpoints for cluster
and service kerberos descriptors
Repository: ambari
Updated Branches:
refs/heads/trunk e2b3f4deb -> d902509f7
AMBARI-9142. Create new API endpoints for cluster and service kerberos descriptors
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/d902509f
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/d902509f
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/d902509f
Branch: refs/heads/trunk
Commit: d902509f755701285bda9bc23b51bd2e58064133
Parents: e2b3f4d
Author: John Speidel <js...@hortonworks.com>
Authored: Wed Jan 14 22:34:49 2015 -0500
Committer: John Speidel <js...@hortonworks.com>
Committed: Thu Jan 15 13:27:42 2015 -0500
----------------------------------------------------------------------
.../resources/ClusterResourceDefinition.java | 2 +
.../resources/ResourceInstanceFactoryImpl.java | 4 +
.../resources/ServiceResourceDefinition.java | 3 +
.../server/api/services/ClusterService.java | 95 ++-
.../server/api/services/ServiceService.java | 92 ++-
.../internal/ArtifactResourceProvider.java | 695 +++++++++++++++++++
.../internal/DefaultProviderModule.java | 2 +
.../ambari/server/controller/spi/Resource.java | 4 +-
.../ambari/server/orm/dao/ArtifactDAO.java | 109 +++
.../server/orm/entities/ArtifactEntity.java | 137 ++++
.../server/orm/entities/ArtifactEntityPK.java | 98 +++
.../server/upgrade/UpgradeCatalog200.java | 10 +
.../main/resources/Ambari-DDL-MySQL-CREATE.sql | 6 +
.../main/resources/Ambari-DDL-Oracle-CREATE.sql | 6 +
.../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql | 7 +
.../src/main/resources/META-INF/persistence.xml | 4 +-
.../api/query/render/DefaultRendererTest.java | 3 +-
.../api/query/render/MinimalRendererTest.java | 3 +-
.../ClusterResourceDefinitionTest.java | 3 +-
.../ResourceInstanceFactoryImplTest.java | 10 +
.../ServiceResourceDefinitionTest.java | 64 ++
.../internal/ArtifactResourceProviderTest.java | 229 ++++++
.../server/upgrade/UpgradeCatalog200Test.java | 37 +
23 files changed, 1616 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
index 422cf1c..9b744d0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
@@ -70,6 +70,8 @@ public class ClusterResourceDefinition extends BaseStacksResourceDefinition {
setChildren.add(new SubResourceDefinition(Resource.Type.AlertDefinition));
setChildren.add(new SubResourceDefinition(Resource.Type.Alert));
setChildren.add(new SubResourceDefinition(Resource.Type.ClusterStackVersion));
+ //todo: dynamic sub-resource definition
+ setChildren.add(new SubResourceDefinition(Resource.Type.Artifact));
return setChildren;
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
index a75729d..fc28c13 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
@@ -356,6 +356,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
};
break;
+ case Artifact:
+ resourceDefinition = new SimpleResourceDefinition(Resource.Type.Artifact, "artifact", "artifacts");
+ break;
+
default:
throw new IllegalArgumentException("Unsupported resource type: " + type);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java
index be8e0b4..9abf3a7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceResourceDefinition.java
@@ -50,6 +50,9 @@ public class ServiceResourceDefinition extends BaseResourceDefinition {
Set<SubResourceDefinition> subs = new HashSet<SubResourceDefinition>();
subs.add(new SubResourceDefinition(Resource.Type.Component));
subs.add(new SubResourceDefinition(Resource.Type.Alert));
+ //todo: dynamic sub-resource definition
+ subs.add(new SubResourceDefinition(Resource.Type.Artifact));
+
return subs;
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/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 ead49ca..5f44b2f 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
@@ -19,6 +19,8 @@
package org.apache.ambari.server.api.services;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@@ -170,6 +172,81 @@ public class ClusterService extends BaseService {
}
/**
+ * Handles: GET /clusters/{clusterID}/artifacts
+ * Get all artifacts associated with the cluster.
+ *
+ * @param body request body
+ * @param headers http headers
+ * @param ui uri info
+ * @param clusterName cluster name
+ *
+ * @return artifact collection resource representation
+ */
+ @GET
+ @Path("{clusterName}/artifacts")
+ @Produces("text/plain")
+ public Response getClusterArtifacts(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,
+ createArtifactResource(clusterName, null));
+ }
+
+ /**
+ * Handles: GET /clusters/{clusterID}/artifacts/{artifactName}
+ * Get an artifact resource instance.
+ *
+ * @param body request body
+ * @param headers http headers
+ * @param ui uri info
+ * @param clusterName cluster name
+ * @param artifactName artifact name
+ *
+ * @return artifact instance resource representation
+ */
+ @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) {
+
+ hasPermission(Request.Type.GET, clusterName);
+ return handleRequest(headers, body, ui, Request.Type.GET,
+ createArtifactResource(clusterName, artifactName));
+ }
+
+ /**
+ * Handles: POST /clusters/{clusterID}/artifacts/{artifactName}
+ * Create a cluster artifact.
+ *
+ * @param body request body
+ * @param headers http headers
+ * @param ui uri info
+ * @param clusterName cluster name
+ * @param artifactName artifact name
+ * @return
+ */
+ @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) {
+
+ hasPermission(Request.Type.POST, clusterName);
+ return handleRequest(headers, body, ui, Request.Type.POST,
+ createArtifactResource(clusterName, artifactName));
+ }
+
+ /**
* Get the hosts sub-resource
*
* @param request the request
@@ -434,7 +511,7 @@ public class ClusterService extends BaseService {
* Gets the services for upgrades.
*
* @param request the request
- * @param cluserName the cluster name
+ * @param clusterName the cluster name
*
* @return the upgrade services
*/
@@ -476,6 +553,22 @@ public class ClusterService extends BaseService {
}
/**
+ * Create an artifact resource instance.
+ *
+ * @param clusterName cluster name
+ * @param artifactName artifact name
+ *
+ * @return an artifact resource instance
+ */
+ ResourceInstance createArtifactResource(String clusterName, String artifactName) {
+ Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+ mapIds.put(Resource.Type.Cluster, clusterName);
+ mapIds.put(Resource.Type.Artifact, artifactName);
+
+ return createResource(Resource.Type.Artifact, mapIds);
+ }
+
+ /**
* Determine whether or not the access specified by the given request type is
* permitted for the current user on the cluster resource identified by the
* given cluster name.
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/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 a51b50b..8a2642b 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
@@ -200,6 +200,79 @@ public class ServiceService extends BaseService {
}
/**
+ * Handles: POST /clusters/{clusterId}/services/{serviceId}/artifacts/{artifactName}
+ * Create a service artifact instance.
+ *
+ * @param body http body
+ * @param headers http headers
+ * @param ui uri info
+ * @param serviceName service name
+ * @param artifactName artifact name
+ *
+ * @return information regarding the created artifact
+ */
+ @POST
+ @Path("{serviceName}/artifacts/{artifactName}")
+ @Produces("text/plain")
+ public Response createArtifact(String body,
+ @Context HttpHeaders headers,
+ @Context UriInfo ui,
+ @PathParam("serviceName") String serviceName,
+ @PathParam("artifactName") String artifactName) {
+
+ return handleRequest(headers, body, ui, Request.Type.POST,
+ createArtifactResource(m_clusterName, serviceName, artifactName));
+ }
+
+ /**
+ * Handles: GET /clusters/{clusterId}/services/{serviceId}/artifacts
+ * Get all service artifacts.
+ *
+ * @param body http body
+ * @param headers http headers
+ * @param ui uri info
+ * @param serviceName service name
+ *
+ * @return artifact collection resource representation
+ */
+ @GET
+ @Path("{serviceName}/artifacts")
+ @Produces("text/plain")
+ public Response getArtifacts(String body,
+ @Context HttpHeaders headers,
+ @Context UriInfo ui,
+ @PathParam("serviceName") String serviceName) {
+
+ return handleRequest(headers, body, ui, Request.Type.GET,
+ createArtifactResource(m_clusterName, serviceName, null));
+ }
+
+ /**
+ * Handles: GET /clusters/{clusterId}/services/{serviceId}/artifacts/{artifactName}
+ * Gat a service artifact instance.
+ *
+ * @param body http body
+ * @param headers http headers
+ * @param ui uri info
+ * @param serviceName service name
+ * @param artifactName artifact name
+ *
+ * @return artifact instance resource representation
+ */
+ @GET
+ @Path("{serviceName}/artifacts/{artifactName}")
+ @Produces("text/plain")
+ public Response getArtifact(String body,
+ @Context HttpHeaders headers,
+ @Context UriInfo ui,
+ @PathParam("serviceName") String serviceName,
+ @PathParam("artifactName") String artifactName) {
+
+ return handleRequest(headers, body, ui, Request.Type.GET,
+ createArtifactResource(m_clusterName, serviceName, artifactName));
+ }
+
+ /**
* Gets the alert history service
*
* @param request
@@ -220,7 +293,6 @@ public class ServiceService extends BaseService {
/**
* Create a service resource instance.
*
- *
* @param clusterName cluster name
* @param serviceName service name
*
@@ -233,4 +305,22 @@ public class ServiceService extends BaseService {
return createResource(Resource.Type.Service, mapIds);
}
+
+ /**
+ * Create an artifact resource instance.
+ *
+ * @param clusterName cluster name
+ * @param serviceName service name
+ * @param artifactName artifact name
+ *
+ * @return an artifact resource instance
+ */
+ ResourceInstance createArtifactResource(String clusterName, String serviceName, String artifactName) {
+ Map<Resource.Type,String> mapIds = new HashMap<Resource.Type, String>();
+ mapIds.put(Resource.Type.Cluster, clusterName);
+ mapIds.put(Resource.Type.Service, serviceName);
+ mapIds.put(Resource.Type.Artifact, artifactName);
+
+ return createResource(Resource.Type.Artifact, mapIds);
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/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
new file mode 100644
index 0000000..b3eb159
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ArtifactResourceProvider.java
@@ -0,0 +1,695 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.controller.internal;
+
+import com.google.inject.Inject;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.DuplicateResourceException;
+import org.apache.ambari.server.ObjectNotFoundException;
+import org.apache.ambari.server.ParentObjectNotFoundException;
+import org.apache.ambari.server.StaticallyInject;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.orm.dao.ArtifactDAO;
+import org.apache.ambari.server.orm.entities.ArtifactEntity;
+import org.apache.ambari.server.state.Cluster;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * Provider for cluster artifacts.
+ * Artifacts contain an artifact name as the PK and artifact data in the form of
+ * a map which is the content of the artifact.
+ * <p>
+ * An example of an artifact is a kerberos descriptor.
+ */
+//todo: implement ExtendedResourceProvider???
+@StaticallyInject
+public class ArtifactResourceProvider extends AbstractResourceProvider {
+ /**
+ * artifact name
+ */
+ public static final String ARTIFACT_NAME_PROPERTY =
+ PropertyHelper.getPropertyId("Artifacts", "artifact_name");
+
+ /**
+ * artifact data
+ */
+ public static final String ARTIFACT_DATA_PROPERTY = "artifact_data";
+
+ /**
+ * primary key fields
+ */
+ private static Set<String> pkPropertyIds = new HashSet<String>();
+
+ /**
+ * map of resource type to fk field
+ */
+ private static Map<Resource.Type, String> keyPropertyIds =
+ new HashMap<Resource.Type, String>();
+
+ /**
+ * resource properties
+ */
+ private static Set<String> propertyIds = new HashSet<String>();
+
+ /**
+ * map of resource type to type registration
+ */
+ private static final Map<Resource.Type, TypeRegistration> typeRegistrations =
+ new HashMap<Resource.Type, TypeRegistration>();
+
+ /**
+ * map of foreign key field to type registration
+ */
+ private static final Map<String, TypeRegistration> typeRegistrationsByFK =
+ new HashMap<String, TypeRegistration>();
+
+ /**
+ * map of short foreign key field to type registration
+ */
+ private static final Map<String, TypeRegistration> typeRegistrationsByShortFK =
+ new HashMap<String, TypeRegistration>();
+
+ /**
+ * artifact data access object
+ */
+ @Inject
+ private static ArtifactDAO artifactDAO;
+
+
+ /**
+ * set resource properties, pk and fk's
+ */
+ static {
+ // resource properties
+ propertyIds.add(ARTIFACT_NAME_PROPERTY);
+ propertyIds.add(ARTIFACT_DATA_PROPERTY);
+
+ // pk property
+ pkPropertyIds.add(ARTIFACT_NAME_PROPERTY);
+
+ // key properties
+ keyPropertyIds.put(Resource.Type.Artifact, ARTIFACT_NAME_PROPERTY);
+
+ //todo: external registration
+ // cluster registration
+ ClusterTypeRegistration clusterTypeRegistration = new ClusterTypeRegistration();
+ typeRegistrations.put(clusterTypeRegistration.getType(), clusterTypeRegistration);
+
+ //service registration
+ ServiceTypeRegistration serviceTypeRegistration = new ServiceTypeRegistration();
+ typeRegistrations.put(serviceTypeRegistration.getType(), serviceTypeRegistration);
+
+ //todo: detect resource type and fk name collisions during registration
+ for (TypeRegistration registration: typeRegistrations.values()) {
+ String fkProperty = registration.getFKPropertyName();
+ keyPropertyIds.put(registration.getType(), fkProperty);
+ propertyIds.add(fkProperty);
+
+ typeRegistrationsByFK.put(fkProperty, registration);
+ typeRegistrationsByShortFK.put(registration.getShortFKPropertyName(), registration);
+
+ for (Map.Entry<Resource.Type, String> ancestor : registration.getForeignKeyInfo().entrySet()) {
+ Resource.Type ancestorType = ancestor.getKey();
+ if (! keyPropertyIds.containsKey(ancestorType)) {
+ String ancestorFK = ancestor.getValue();
+ keyPropertyIds.put(ancestorType, ancestorFK);
+ propertyIds.add(ancestorFK);
+ }
+ }
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param controller management controller
+ */
+ @Inject
+ protected ArtifactResourceProvider(AmbariManagementController controller) {
+ super(propertyIds, keyPropertyIds);
+
+ for (TypeRegistration typeRegistration : typeRegistrations.values()) {
+ typeRegistration.setManagementController(controller);
+ }
+ }
+
+ @Override
+ protected Set<String> getPKPropertyIds() {
+ return pkPropertyIds;
+ }
+
+ @Override
+ public RequestStatus createResources(Request request)
+ throws SystemException,
+ UnsupportedPropertyException,
+ ResourceAlreadyExistsException,
+ NoSuchParentResourceException {
+
+ for (Map<String, Object> properties : request.getProperties()) {
+ createResources(getCreateCommand(properties));
+ }
+ notifyCreate(Resource.Type.Artifact, request);
+
+ return getRequestStatus(null);
+ }
+
+ @Override
+ public Set<Resource> getResources(Request request, Predicate predicate)
+ throws SystemException,
+ UnsupportedPropertyException,
+ NoSuchResourceException,
+ NoSuchParentResourceException {
+
+ Set<Map<String, Object>> requestProps = getPropertyMaps(predicate);
+ Set<Resource> resources = new LinkedHashSet<Resource>();
+
+ for (Map<String, Object> props : requestProps) {
+ resources.addAll(getResources(getGetCommand(request, predicate, props)));
+ }
+
+ if (resources.isEmpty() && isInstanceRequest(requestProps)) {
+ throw new NoSuchResourceException(
+ "The requested resource doesn't exist: Artifact not found, " + predicate);
+ }
+ return resources;
+ }
+
+ @Override
+ public RequestStatus updateResources(Request request, Predicate predicate)
+ throws SystemException,
+ UnsupportedPropertyException,
+ NoSuchResourceException,
+ NoSuchParentResourceException {
+
+ throw new UnsupportedOperationException("Update not currently supported for Artifact resources");
+ }
+
+ @Override
+ public RequestStatus deleteResources(Predicate predicate)
+ throws SystemException,
+ UnsupportedPropertyException,
+ NoSuchResourceException,
+ NoSuchParentResourceException {
+
+ throw new UnsupportedOperationException("Delete not currently supported for Artifact resources");
+ }
+
+ /**
+ * Create a command to create the resource.
+ *
+ * @param properties request properties
+ *
+ * @return a new create command
+ */
+ private Command<Void> getCreateCommand(final Map<String, Object> properties) {
+ return new Command<Void>() {
+ @Override
+ public Void invoke() throws AmbariException {
+ // ensure that parent exists
+ validateParent(properties);
+
+ String artifactName = String.valueOf(properties.get(ARTIFACT_NAME_PROPERTY));
+ TreeMap<String, String> foreignKeyMap = createForeignKeyMap(properties);
+
+ if (artifactDAO.findByNameAndForeignKeys(artifactName, foreignKeyMap) != null) {
+ throw new DuplicateResourceException(String.format(
+ "Attempted to create an artifact which already exists, artifact_name='%s', foreign_keys='%s'",
+ artifactName, getRequestForeignKeys(properties)));
+ }
+
+ LOG.debug("Creating Artifact Resource with name '{}'. Parent information: {}",
+ artifactName, getRequestForeignKeys(properties));
+
+ artifactDAO.create(toEntity(properties));
+
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Create a command to get the requested resources.
+ *
+ * @param properties request properties
+ *
+ * @return a new get command
+ */
+ private Command<Set<Resource>> getGetCommand(final Request request,
+ final Predicate predicate,
+ final Map<String, Object> properties) {
+ return new Command<Set<Resource>>() {
+ @Override
+ public Set<Resource> invoke() throws AmbariException {
+ String name = (String) properties.get(ARTIFACT_NAME_PROPERTY);
+ validateParent(properties);
+
+ Set<Resource> matchingResources = new HashSet<Resource>();
+ TreeMap<String, String> foreignKeys = createForeignKeyMap(properties);
+ Set<String> requestPropertyIds = getRequestPropertyIds(request, predicate);
+ if (name != null) {
+ // find instance using name and foreign keys
+ ArtifactEntity entity = artifactDAO.findByNameAndForeignKeys(name, foreignKeys);
+ if (entity != null) {
+ Resource instance = (toResource(entity, requestPropertyIds));
+ if (predicate.evaluate(instance)) {
+ matchingResources.add(instance);
+ }
+ }
+ } else {
+ // find collection using foreign keys only
+ List<ArtifactEntity> results = artifactDAO.findByForeignKeys(foreignKeys);
+ for (ArtifactEntity entity : results) {
+ Resource resource = toResource(entity, requestPropertyIds);
+ if (predicate.evaluate(resource)) {
+ matchingResources.add(resource);
+ }
+ }
+ }
+ return matchingResources;
+ }
+ };
+ }
+
+ /**
+ * Validate that parent resources exist.
+ *
+ * @param properties request properties
+ *
+ * @throws ParentObjectNotFoundException if the parent resource doesn't exist
+ * @throws AmbariException if an error occurred while attempting to validate the parent
+ */
+ private void validateParent(Map<String, Object> properties) throws AmbariException {
+ Resource.Type parentType = getRequestType(properties);
+ if (! typeRegistrations.get(parentType).instanceExists(keyPropertyIds, properties)) {
+ throw new ParentObjectNotFoundException(String.format(
+ "Parent resource doesn't exist: %s", getRequestForeignKeys(properties)));
+ }
+ }
+
+ /**
+ * Get the type of the parent resource from the request properties.
+ *
+ * @param properties request properties
+ *
+ * @return the parent resource type based on the request properties
+ *
+ * @throws AmbariException if unable to determine the parent resource type
+ */
+ private Resource.Type getRequestType(Map<String, Object> properties) throws AmbariException {
+ Set<String> requestFKs = getRequestForeignKeys(properties).keySet();
+ for (TypeRegistration registration : typeRegistrations.values()) {
+ Collection<String> typeFKs = new HashSet<String>(registration.getForeignKeyInfo().values());
+ typeFKs.add(registration.getFKPropertyName());
+ if (requestFKs.equals(typeFKs)) {
+ return registration.getType();
+ }
+ }
+ throw new AmbariException("Couldn't determine resource type based on request properties");
+ }
+
+ /**
+ * Get a map of foreign key to value for the given request properties.
+ * The foreign key map will only include the foreign key properties which
+ * are included in the request properties. This is useful for reporting
+ * errors back to the user.
+ * .
+ * @param properties request properties
+ *
+ * @return map of foreign key to value for the provided request properties
+ */
+ private Map<String, String> getRequestForeignKeys(Map<String, Object> properties) {
+ Map<String, String> requestFKs = new HashMap<String, String>();
+ for (String property : properties.keySet()) {
+ if (! property.equals(ARTIFACT_NAME_PROPERTY) && ! property.startsWith(ARTIFACT_DATA_PROPERTY)) {
+ requestFKs.put(property, String.valueOf(properties.get(property)));
+ }
+ }
+ return requestFKs;
+ }
+
+ /**
+ * Convert a map of properties to an artifact entity.
+ *
+ * @param properties property map
+ *
+ * @return new artifact entity
+ */
+ private ArtifactEntity toEntity(Map<String, Object> properties)
+ throws AmbariException {
+
+ String name = (String) properties.get(ARTIFACT_NAME_PROPERTY);
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException("Artifact name must be provided");
+ }
+
+ ArtifactEntity artifact = new ArtifactEntity();
+ artifact.setArtifactName(name);
+ Map<String, Object> dataMap = new HashMap<String, Object>();
+ for (Map.Entry<String, Object> entry : properties.entrySet()) {
+ String key = entry.getKey();
+ //todo: should we handle scalar value?
+ if (key.startsWith(ARTIFACT_DATA_PROPERTY)) {
+ dataMap.put(key.split("/")[1], entry.getValue());
+ }
+ }
+ artifact.setArtifactData(dataMap);
+ artifact.setForeignKeys(createForeignKeyMap(properties));
+
+ return artifact;
+ }
+
+ /**
+ * Create a map of foreign keys and values which can be persisted.
+ * This map will include the short fk names of the key properties as well
+ * as the 'persist id' representation of the value which is returned
+ * by the type registration.
+ *
+ * @param properties request properties
+ * @return an ordered map of key name to value
+ *
+ * @throws AmbariException an unexpected exception occurred
+ */
+ private TreeMap<String, String> createForeignKeyMap(Map<String, Object> properties) throws AmbariException {
+ TreeMap<String, String> foreignKeys = new TreeMap<String, String>();
+ for (String keyProperty : keyPropertyIds.values()) {
+ if (! keyProperty.equals(ARTIFACT_NAME_PROPERTY)) {
+ String origValue = (String) properties.get(keyProperty);
+ if (origValue != null && ! origValue.isEmpty()) {
+ TypeRegistration typeRegistration = typeRegistrationsByFK.get(keyProperty);
+ foreignKeys.put(typeRegistration.getShortFKPropertyName(), typeRegistration.toPersistId(origValue));
+ }
+ }
+ }
+ return foreignKeys;
+ }
+
+ /**
+ * Create a resource instance from an artifact entity.
+ * This will convert short fk property names to the full property name as well
+ * as converting the value from the 'persist id' representation which is written
+ * to the database.
+ *
+ * @param entity artifact entity
+ * @param requestedIds requested id's
+ *
+ * @return a new resource instance for the given artifact entity
+ */
+ private Resource toResource(ArtifactEntity entity, Set<String> requestedIds) throws AmbariException {
+ Resource resource = new ResourceImpl(Resource.Type.Artifact);
+ setResourceProperty(resource, ARTIFACT_NAME_PROPERTY, entity.getArtifactName(), requestedIds);
+ setResourceProperty(resource, ARTIFACT_DATA_PROPERTY, entity.getArtifactData(), requestedIds);
+
+ for (Map.Entry<String, String> entry : entity.getForeignKeys().entrySet()) {
+ TypeRegistration typeRegistration = typeRegistrationsByShortFK.get(entry.getKey());
+ setResourceProperty(resource, typeRegistration.getFKPropertyName(),
+ typeRegistration.fromPersistId(entry.getValue()), requestedIds);
+ }
+ return resource;
+ }
+
+ /**
+ * Determine if the request was for an instance resource.
+ *
+ * @param requestProps request properties
+ *
+ * @return true if the request was for a specific instance, false otherwise
+ */
+ private boolean isInstanceRequest(Set<Map<String, Object>> requestProps) {
+ return requestProps.size() == 1 &&
+ requestProps.iterator().next().get(ARTIFACT_NAME_PROPERTY) != null;
+ }
+
+ //todo: when static registration is changed to external registration, this interface
+ //todo: should be extracted as a first class interface.
+ /**
+ * Used to register a dynamic sub-resource with an existing resource type.
+ */
+ public static interface TypeRegistration {
+ /**
+ * Allows the management controller to be set on the registration.
+ * This is called as part of the registration process.
+ * For registrations that need access to the management controller,
+ * they should assign this controller to a member field.
+ *
+ * @param controller management controller
+ */
+ public void setManagementController(AmbariManagementController controller);
+
+ /**
+ * Get the type of the registering resource.
+ *
+ * @return type of the register resource
+ */
+ public Resource.Type getType();
+
+ /**
+ * Full foreign key property name to use in the artifact resource.
+ * At this time, all foreign key properties should be in the "Artifacts" category.
+ *
+ * @return the absolute foreign key property name.
+ * For example: "Artifacts/cluster_name
+ */
+ //todo: use relative property names
+ public String getFKPropertyName();
+
+ /**
+ * Shortened foreign key name that is written to the database.
+ * This name doesn't need to be in any category but must be unique
+ * across all registrations.
+ *
+ * @return short fk name. For example: "cluster_name"
+ */
+ public String getShortFKPropertyName();
+
+ /**
+ * Convert the foreign key value to a value that is persisted to the database.
+ * In most cases this will be the original value.
+ * <p>
+ * An example of when this will be different is when the fk value value needs
+ * to be converted to the unique id for the resource.
+ * <p>
+ * For example, the cluster_name to the cluster_id.
+ * <p>
+ * This returned value will later be converted back to the normal form via
+ * {@link #fromPersistId(String)}.
+ *
+ * @param value normal form of the fk value used by the api
+ *
+ * @return persist id form of the fk value
+ *
+ * @throws AmbariException if unable to convert the value
+ */
+ public String toPersistId(String value) throws AmbariException;
+
+ /**
+ * Convert the persist id form of the foreign key which is written to the database
+ * to the form used by the api. In most cases, this will be the same.
+ * <p>
+ * This method takes the value returned from {@link #toPersistId(String)} and converts
+ * it back to the original value which is used by the api.
+ * <p>
+ * An example of this is the converting the cluster name to the cluster id in
+ * {@link #toPersistId(String)} and then back to the cluster name by this method. The
+ * api always uses the cluster name so we wouldn't want to return the id back as the
+ * value for a cluster_name foreign key.
+ *
+ * @param value persist id form of the fk value
+ *
+ * @return normal form of the fk value used by the api
+ *
+ * @throws AmbariException if unable to convert the value
+ */
+ public String fromPersistId(String value) throws AmbariException;
+
+ /**
+ * Get a map of ancestor type to foreign key.
+ * <p>
+ * <b>Note: Currently, if a parent resource has also registered the same dynamic resource,
+ * the foreign key name used here has to match the value returned by the parent resource
+ * in {@link #getFKPropertyName()}</b>
+ *
+ * @return map of ancestor type to foreign key
+ */
+ //todo: look at the need to use the same name as specified by ancestors
+ public Map<Resource.Type, String> getForeignKeyInfo();
+
+ /**
+ * Determine if the instance identified by the provided properties exists.
+ *
+ * @param keyMap map of resource type to foreign key properties
+ * @param properties request properties
+ *
+ * @return true if the resource instance exists, false otherwise
+ *
+ * @throws AmbariException an exception occurs trying to determine if the instance exists
+ */
+ public boolean instanceExists(Map<Resource.Type, String> keyMap,
+ Map<String, Object> properties) throws AmbariException;
+ }
+
+
+ //todo: Registration should be done externally and these implementations should be moved
+ //todo: to a location where the registering resource definition has access to them.
+ /**
+ * Cluster resource registration.
+ */
+ private static class ClusterTypeRegistration implements TypeRegistration {
+ /**
+ * management controller instance
+ */
+ private AmbariManagementController controller = null;
+
+ /**
+ * cluster name property name
+ */
+ private static final String CLUSTER_NAME = PropertyHelper.getPropertyId("Artifacts", "cluster_name");
+
+ @Override
+ public void setManagementController(AmbariManagementController controller) {
+ this.controller = controller;
+ }
+
+ @Override
+ public Resource.Type getType() {
+ return Resource.Type.Cluster;
+ }
+
+ @Override
+ public String getFKPropertyName() {
+ return CLUSTER_NAME;
+ }
+
+ @Override
+ public String getShortFKPropertyName() {
+ return "cluster";
+ }
+
+ @Override
+ public String toPersistId(String value) throws AmbariException {
+ return String.valueOf(controller.getClusters().getCluster(value).getClusterId());
+ }
+
+ @Override
+ public String fromPersistId(String value) throws AmbariException {
+ return controller.getClusters().getClusterById(Long.valueOf(value)).getClusterName();
+ }
+
+ @Override
+ public Map<Resource.Type, String> getForeignKeyInfo() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public boolean instanceExists(Map<Resource.Type, String> keyMap,
+ Map<String, Object> properties) throws AmbariException {
+ try {
+ String clusterName = String.valueOf(properties.get(CLUSTER_NAME));
+ controller.getClusters().getCluster(clusterName);
+ return true;
+ } catch (ObjectNotFoundException e) {
+ // doesn't exist
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Service resource registration.
+ */
+ private static class ServiceTypeRegistration implements TypeRegistration {
+ /**
+ * management controller instance
+ */
+ private AmbariManagementController controller = null;
+
+ /**
+ * service name property name
+ */
+ private static final String SERVICE_NAME = PropertyHelper.getPropertyId("Artifacts", "service_name");
+
+ @Override
+ public void setManagementController(AmbariManagementController controller) {
+ this.controller = controller;
+ }
+
+ @Override
+ public Resource.Type getType() {
+ return Resource.Type.Service;
+ }
+
+ @Override
+ public String getFKPropertyName() {
+ return SERVICE_NAME;
+ }
+
+ @Override
+ public String getShortFKPropertyName() {
+ return "service";
+ }
+
+ @Override
+ public String toPersistId(String value) {
+ return value;
+ }
+
+ @Override
+ public String fromPersistId(String value) {
+ return value;
+ }
+
+ @Override
+ public Map<Resource.Type, String> getForeignKeyInfo() {
+ return Collections.singletonMap(Resource.Type.Cluster, "Artifacts/cluster_name");
+ }
+
+ @Override
+ public boolean instanceExists(Map<Resource.Type, String> keyMap,
+ Map<String, Object> properties) throws AmbariException {
+
+ String clusterName = String.valueOf(properties.get(keyMap.get(Resource.Type.Cluster)));
+ try {
+ Cluster cluster = controller.getClusters().getCluster(clusterName);
+ cluster.getService(String.valueOf(properties.get(SERVICE_NAME)));
+ return true;
+ } catch (ObjectNotFoundException e) {
+ // doesn't exist
+ }
+ return false;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
index be2a9ad..b0e23e9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
@@ -114,6 +114,8 @@ public class DefaultProviderModule extends AbstractProviderModule {
return new OperatingSystemResourceProvider(managementController);
case Repository:
return new RepositoryResourceProvider(managementController);
+ case Artifact:
+ return new ArtifactResourceProvider(managementController);
default:
return AbstractControllerResourceProvider.getResourceProvider(type, propertyIds,
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
index 740f37e..aa5cf64 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
@@ -136,7 +136,8 @@ public interface Resource {
UpgradeItem,
PreUpgradeCheck,
Stage,
- StackArtifact;
+ StackArtifact,
+ Artifact;
/**
* Get the {@link Type} that corresponds to this InternalType.
@@ -234,6 +235,7 @@ public interface Resource {
public static final Type PreUpgradeCheck = InternalType.PreUpgradeCheck.getType();
public static final Type Stage = InternalType.Stage.getType();
public static final Type StackArtifact = InternalType.StackArtifact.getType();
+ public static final Type Artifact = InternalType.Artifact.getType();
/**
* The type name.
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/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
new file mode 100644
index 0000000..4b7cf25
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ArtifactDAO.java
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.orm.dao;
+
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.ArtifactEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+
+/**
+ * Cluster Artifact Data Access Object.
+ */
+@Singleton
+public class ArtifactDAO {
+ /**
+ * JPA entity manager
+ */
+ @Inject
+ Provider<EntityManager> entityManagerProvider;
+
+ /**
+ * Find an artifact with the given name and foreign keys.
+ *
+ * @param artifactName name of artifact to find
+ * @param foreignKeys foreign keys of artifact as json representation of
+ * a map of key properties to values
+ *
+ * @return a matching artifact or null
+ */
+ @RequiresSession
+ public ArtifactEntity findByNameAndForeignKeys(String artifactName, TreeMap<String, String> foreignKeys) {
+ //todo: need to update PK in DB
+ TypedQuery<ArtifactEntity> query = entityManagerProvider.get()
+ .createNamedQuery("artifactByNameAndForeignKeys", ArtifactEntity.class);
+ query.setParameter("artifactName", artifactName);
+ query.setParameter("foreignKeys", ArtifactEntity.serializeForeignKeys(foreignKeys));
+
+ try {
+ return query.getSingleResult();
+ } catch (NoResultException ignored) {
+ return null;
+ }
+ }
+
+ /**
+ * Find all artifacts for the specified foreign keys.
+ *
+ * @param foreignKeys foreign keys of artifact as json representation of
+ * a map of key properties to values
+ *
+ * @return all artifacts for the specified foreign keys or an empty List
+ */
+ @RequiresSession
+ public List<ArtifactEntity> findByForeignKeys(TreeMap<String, String> foreignKeys) {
+ TypedQuery<ArtifactEntity> query = entityManagerProvider.get().
+ createNamedQuery("artifactByForeignKeys", ArtifactEntity.class);
+ query.setParameter("foreignKeys", ArtifactEntity.serializeForeignKeys(foreignKeys));
+
+ return query.getResultList();
+ }
+
+ /**
+ * Refresh the state of the instance from the database,
+ * overwriting changes made to the entity, if any.
+ *
+ * @param entity entity to refresh
+ */
+ @Transactional
+ public void refresh(ArtifactEntity entity) {
+ entityManagerProvider.get().refresh(entity);
+ }
+
+ /**
+ * Make an instance managed and persistent.
+ *
+ * @param entity entity to persist
+ */
+ @Transactional
+ public void create(ArtifactEntity entity) {
+ entityManagerProvider.get().persist(entity);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntity.java
new file mode 100644
index 0000000..849a938
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntity.java
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.orm.entities;
+
+import com.google.gson.Gson;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Entity representing an Artifact.
+ */
+@IdClass(ArtifactEntityPK.class)
+@Table(name = "artifact")
+@NamedQueries({
+ @NamedQuery(name = "artifactByNameAndForeignKeys",
+ query = "SELECT artifact FROM ArtifactEntity artifact " +
+ "WHERE artifact.artifactName=:artifactName AND artifact.foreignKeys=:foreignKeys"),
+ @NamedQuery(name = "artifactByForeignKeys",
+ query = "SELECT artifact FROM ArtifactEntity artifact " +
+ "WHERE artifact.foreignKeys=:foreignKeys")
+})
+
+@Entity
+public class ArtifactEntity {
+ @Id
+ @Column(name = "artifact_name", nullable = false, insertable = true, updatable = false, unique = true)
+ private String artifactName;
+
+ @Id
+ @Column(name = "foreign_keys", nullable = false, insertable = true, updatable = false)
+ @Basic
+ private String foreignKeys;
+
+ @Column(name = "artifact_data", nullable = false, insertable = true, updatable = false)
+ @Basic
+ private String artifactData;
+
+ @Transient
+ private static final Gson jsonSerializer = new Gson();
+
+
+ /**
+ * Get the artifact name.
+ *
+ * @return artifact name
+ */
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ /**
+ * Set the artifact name.
+ *
+ * @param artifactName the artifact name
+ */
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ /**
+ * Set the artifact data by specifying a map that is then
+ * converted to a json string.
+ *
+ * @param artifactData artifact data map
+ */
+ public void setArtifactData(Map<String, Object> artifactData) {
+ this.artifactData = jsonSerializer.toJson(artifactData);
+ }
+
+ /**
+ * Get the artifact data as a map
+ *
+ * @return artifact data as a map
+ */
+ public Map<String, Object> getArtifactData() {
+ return jsonSerializer.<Map<String, Object>>fromJson(
+ artifactData, Map.class);
+ }
+
+ /**
+ * Set the foreign keys.
+ *
+ * @param foreignKeys ordered map of foreign key property names to values
+ */
+ public void setForeignKeys(TreeMap<String, String> foreignKeys) {
+ this.foreignKeys = serializeForeignKeys(foreignKeys);
+ }
+
+ /**
+ * Get the foreign keys.
+ *
+ * @return foreign key map of property name to value
+ */
+ public Map<String, String> getForeignKeys() {
+ return foreignKeys == null ?
+ Collections.<String, String>emptyMap() :
+ jsonSerializer.<Map<String, String>>fromJson(foreignKeys, Map.class);
+ }
+
+ /**
+ * Serialize a map of foreign keys to a string.
+ *
+ * @param foreignKeys map of foreign keys to values
+ *
+ * @return string representation of the foreign keys map
+ */
+ public static String serializeForeignKeys(TreeMap<String, String> foreignKeys) {
+ return jsonSerializer.toJson(foreignKeys);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntityPK.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntityPK.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntityPK.java
new file mode 100644
index 0000000..67b52c9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ArtifactEntityPK.java
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.orm.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Id;
+
+/**
+ * Composite primary key for ArtifactEntity.
+ */
+public class ArtifactEntityPK {
+ @Id
+ @Column(name = "artifact_name", nullable = false, insertable = true, updatable = false)
+ private String artifactName;
+
+ @Id
+ @Column(name = "foreign_keys", nullable = false, insertable = true, updatable = false)
+ private String foreignKeys;
+
+ /**
+ * Constructor.
+ *
+ @param artifactName artifact name
+ @param foreignKeys foreign key information
+ */
+ public ArtifactEntityPK(String artifactName, String foreignKeys) {
+ this.artifactName = artifactName;
+ this.foreignKeys = foreignKeys;
+ }
+
+ /**
+ * Get the name of the associated artifact.
+ *
+ * @return artifact name
+ */
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ /**
+ * Set the name of the associated artifact.
+ *
+ * @param name artifact name
+ */
+ public void setArtifactName(String name) {
+ artifactName = name;
+ }
+
+ /**
+ * Get the foreign key information of the associated artifact.
+ *
+ * @return foreign key information
+ */
+ public String getForeignKeys() {
+ return foreignKeys;
+ }
+
+ /**
+ * Set the foreign key information of the associated artifact.
+ *
+ * @param foreignKeys foreign key information
+ */
+ public void setForeignKeys(String foreignKeys) {
+ this.foreignKeys = foreignKeys;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ArtifactEntityPK that = (ArtifactEntityPK) o;
+
+ return this.artifactName.equals(that.artifactName) &&
+ this.foreignKeys.equals(that.foreignKeys);
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * artifactName.hashCode() + foreignKeys.hashCode();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
index f7bd080..3a043b9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
@@ -63,6 +63,7 @@ public class UpgradeCatalog200 extends AbstractUpgradeCatalog {
private static final String ALERT_TARGET_TABLE = "alert_target";
private static final String ALERT_TARGET_STATES_TABLE = "alert_target_states";
private static final String ALERT_CURRENT_TABLE = "alert_current";
+ private static final String ARTIFACT_TABLE = "artifact";
/**
* {@inheritDoc}
@@ -107,6 +108,7 @@ public class UpgradeCatalog200 extends AbstractUpgradeCatalog {
protected void executeDDLUpdates() throws AmbariException, SQLException {
prepareRollingUpgradesDDL();
executeAlertDDLUpdates();
+ createArtifactTable();
// add security_state to various tables
dbAccessor.addColumn("hostcomponentdesiredstate", new DBColumnInfo(
@@ -255,6 +257,14 @@ public class UpgradeCatalog200 extends AbstractUpgradeCatalog {
dbAccessor.executeQuery("INSERT INTO ambari_sequences(sequence_name, sequence_value) VALUES('upgrade_item_id_seq', 0)", false);
}
+ private void createArtifactTable() throws SQLException {
+ ArrayList<DBColumnInfo> columns = new ArrayList<DBColumnInfo>();
+ columns.add(new DBColumnInfo("artifact_name", String.class, 255, null, false));
+ columns.add(new DBColumnInfo("foreign_keys", String.class, null, null, false));
+ columns.add(new DBColumnInfo("artifact_data", char[].class, null, null, false));
+ dbAccessor.createTable(ARTIFACT_TABLE, columns, "artifact_name", "foreign_keys");
+ }
+
// ----- UpgradeCatalog ----------------------------------------------------
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index 6f3f094..972053c 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -517,6 +517,12 @@ CREATE TABLE repo_version (
PRIMARY KEY(repo_version_id)
);
+CREATE TABLE artifact (
+ artifact_name VARCHAR(255) NOT NULL,
+ foreign_keys LONGTEXT NOT NULL,
+ artifact_data VARCHAR(4096) NOT NULL,
+ PRIMARY KEY(artifact_name, foreign_keys));
+
-- altering tables by creating unique constraints----------
ALTER TABLE users ADD CONSTRAINT UNQ_users_0 UNIQUE (user_name, ldap_user);
ALTER TABLE groups ADD CONSTRAINT UNQ_groups_0 UNIQUE (group_name, ldap_group);
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index 11d5de3..0117fed 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -507,6 +507,12 @@ CREATE TABLE repo_version (
PRIMARY KEY(repo_version_id)
);
+CREATE TABLE artifact (
+ artifact_name VARCHAR2(255) NOT NULL,
+ foreign_keys CLOB NOT NULL,
+ artifact_data VARCHAR2(4096) NOT NULL,
+ PRIMARY KEY(artifact_name, foreign_keys));
+
--------altering tables by creating unique constraints----------
ALTER TABLE users ADD CONSTRAINT UNQ_users_0 UNIQUE (user_name, ldap_user);
ALTER TABLE groups ADD CONSTRAINT UNQ_groups_0 UNIQUE (group_name, ldap_group);
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
index 2ae175b..7c2d7ae 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
@@ -449,6 +449,13 @@ GRANT ALL PRIVILEGES ON TABLE ambari.hostgroup_component TO :username;
GRANT ALL PRIVILEGES ON TABLE ambari.blueprint_configuration TO :username;
GRANT ALL PRIVILEGES ON TABLE ambari.hostgroup_configuration TO :username;
+CREATE TABLE ambari.artifact (
+ artifact_name VARCHAR(255) NOT NULL,
+ artifact_data TEXT NOT NULL,
+ foreign_keys VARCHAR(4096) NOT NULL,
+ PRIMARY KEY (artifact_name, foreign_keys));
+GRANT ALL PRIVILEGES ON TABLE ambari.artifact TO :username;
+
CREATE TABLE ambari.viewmain (
view_name VARCHAR(255) NOT NULL,
label VARCHAR(255),
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/main/resources/META-INF/persistence.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/META-INF/persistence.xml b/ambari-server/src/main/resources/META-INF/persistence.xml
index dc49b7c..07bd67d 100644
--- a/ambari-server/src/main/resources/META-INF/persistence.xml
+++ b/ambari-server/src/main/resources/META-INF/persistence.xml
@@ -75,8 +75,10 @@
<class>org.apache.ambari.server.orm.entities.ViewInstancePropertyEntity</class>
<class>org.apache.ambari.server.orm.entities.ViewParameterEntity</class>
<class>org.apache.ambari.server.orm.entities.ViewResourceEntity</class>
+ <class>org.apache.ambari.server.orm.entities.ArtifactEntity</class>
- <properties>
+
+ <properties>
<!--<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/ambari" />-->
<!--<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />-->
<property name="eclipselink.cache.size.default" value="10000" />
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java
index 3e0617e..4981a67 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/DefaultRendererTest.java
@@ -51,6 +51,7 @@ public class DefaultRendererTest {
// schema expectations
expect(schemaFactory.getSchema(Resource.Type.Component)).andReturn(schema).anyTimes();
expect(schemaFactory.getSchema(Resource.Type.Alert)).andReturn(schema).anyTimes();
+ expect(schemaFactory.getSchema(Resource.Type.Artifact)).andReturn(schema).anyTimes();
expect(schema.getKeyPropertyId(Resource.Type.Component)).andReturn("ServiceComponentInfo/component_name").anyTimes();
expect(schema.getKeyPropertyId(Resource.Type.Service)).andReturn("ServiceComponentInfo/service_name").anyTimes();
@@ -64,7 +65,7 @@ public class DefaultRendererTest {
TreeNode<Set<String>> propertyTree = renderer.finalizeProperties(queryTree, false);
// no properties should have been added
assertTrue(propertyTree.getObject().isEmpty());
- assertEquals(2, propertyTree.getChildren().size());
+ assertEquals(3, propertyTree.getChildren().size());
TreeNode<Set<String>> componentNode = propertyTree.getChild("Component");
assertEquals(2, componentNode.getObject().size());
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java
index 9b03c51..37bf33c 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java
@@ -58,6 +58,7 @@ public class MinimalRendererTest {
// schema expectations
expect(schemaFactory.getSchema(Resource.Type.Component)).andReturn(schema).anyTimes();
expect(schemaFactory.getSchema(Resource.Type.Alert)).andReturn(schema).anyTimes();
+ expect(schemaFactory.getSchema(Resource.Type.Artifact)).andReturn(schema).anyTimes();
expect(schema.getKeyPropertyId(Resource.Type.Component)).andReturn("ServiceComponentInfo/component_name").anyTimes();
replay(schemaFactory, schema);
@@ -70,7 +71,7 @@ public class MinimalRendererTest {
TreeNode<Set<String>> propertyTree = renderer.finalizeProperties(queryTree, false);
// no properties should have been added
assertTrue(propertyTree.getObject().isEmpty());
- assertEquals(2, propertyTree.getChildren().size());
+ assertEquals(3, propertyTree.getChildren().size());
TreeNode<Set<String>> componentNode = propertyTree.getChild("Component");
assertEquals(1, componentNode.getObject().size());
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java
index 6d897b2..cb68dfa 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java
@@ -50,7 +50,7 @@ public class ClusterResourceDefinitionTest {
ResourceDefinition resource = new ClusterResourceDefinition();
Set<SubResourceDefinition> subResources = resource.getSubResourceDefinitions();
- assertEquals(11, subResources.size());
+ assertEquals(12, subResources.size());
assertTrue(includesType(subResources, Resource.Type.Service));
assertTrue(includesType(subResources, Resource.Type.Host));
assertTrue(includesType(subResources, Resource.Type.Configuration));
@@ -62,6 +62,7 @@ public class ClusterResourceDefinitionTest {
assertTrue(includesType(subResources, Resource.Type.ClusterPrivilege));
assertTrue(includesType(subResources, Resource.Type.Alert));
assertTrue(includesType(subResources, Resource.Type.ClusterStackVersion));
+ assertTrue(includesType(subResources, Resource.Type.Artifact));
}
@Test
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
index b5cfcc3..0daffce 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
@@ -37,4 +37,14 @@ public class ResourceInstanceFactoryImplTest {
assertEquals("artifacts", resourceDefinition.getPluralName());
assertEquals(Resource.Type.StackArtifact, resourceDefinition.getType());
}
+
+ @Test
+ public void testGetArtifactDefinition() {
+ ResourceDefinition resourceDefinition = ResourceInstanceFactoryImpl.getResourceDefinition(
+ Resource.Type.Artifact, null);
+
+ assertEquals("artifact", resourceDefinition.getSingularName());
+ assertEquals("artifacts", resourceDefinition.getPluralName());
+ assertEquals(Resource.Type.Artifact, resourceDefinition.getType());
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ServiceResourceDefinitionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ServiceResourceDefinitionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ServiceResourceDefinitionTest.java
new file mode 100644
index 0000000..013c9b9
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ServiceResourceDefinitionTest.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.resources;
+
+import org.apache.ambari.server.controller.spi.Resource;
+import org.junit.Test;
+
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * ServiceResourceDefinition unit tests.
+ */
+public class ServiceResourceDefinitionTest {
+
+ @Test
+ public void testGetPluralName() {
+ assertEquals("services", new ServiceResourceDefinition().getPluralName());
+ }
+
+ @Test
+ public void testGetSingularName() {
+ assertEquals("service", new ServiceResourceDefinition().getSingularName());
+ }
+
+ @Test
+ public void testGetSubResourceDefinitions() {
+ ResourceDefinition resource = new ServiceResourceDefinition();
+ Set<SubResourceDefinition> subResources = resource.getSubResourceDefinitions();
+
+ assertEquals(3, subResources.size());
+ assertTrue(includesType(subResources, Resource.Type.Component));
+ assertTrue(includesType(subResources, Resource.Type.Alert));
+ assertTrue(includesType(subResources, Resource.Type.Artifact));
+ }
+
+ private boolean includesType(Set<SubResourceDefinition> resources, Resource.Type type) {
+ for (SubResourceDefinition subResource : resources) {
+ if (subResource.getType() == type) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/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
new file mode 100644
index 0000000..4ed1a51
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ArtifactResourceProviderTest.java
@@ -0,0 +1,229 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.controller.internal;
+
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PredicateBuilder;
+import org.apache.ambari.server.orm.dao.ArtifactDAO;
+import org.apache.ambari.server.orm.entities.ArtifactEntity;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.easymock.Capture;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.persistence.EntityManager;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+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.TreeMap;
+
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createStrictMock;
+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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * ArtifactResourceProvider unit tests.
+ */
+public class ArtifactResourceProviderTest {
+
+ private ArtifactDAO dao = createStrictMock(ArtifactDAO.class);
+ private EntityManager em = createStrictMock(EntityManager.class);
+ private AmbariManagementController controller = createStrictMock(AmbariManagementController.class);
+ private Request request = createStrictMock(Request.class);
+ private Clusters clusters = createStrictMock(Clusters.class);
+ private Cluster cluster = createStrictMock(Cluster.class);
+ private ArtifactEntity entity = createMock(ArtifactEntity.class);
+ private ArtifactEntity entity2 = createMock(ArtifactEntity.class);
+
+ ArtifactResourceProvider resourceProvider;
+
+ @Before
+ public void setUp() throws Exception {
+ reset(dao, em, controller, request, clusters, cluster, entity, entity2);
+ resourceProvider = new ArtifactResourceProvider(controller);
+ setPrivateField(resourceProvider, "artifactDAO", dao);
+ }
+
+ @Test
+ public void testGetResources_instance() throws Exception {
+ Set<String> propertyIds = new HashSet<String>();
+ TreeMap<String, String> foreignKeys = new TreeMap<String, String>();
+ foreignKeys.put("cluster", "500");
+
+ Map<String, Object> artifact_data = Collections.<String, Object>singletonMap("foo", "bar");
+
+ 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(request.getPropertyIds()).andReturn(propertyIds).anyTimes();
+
+ expect(dao.findByNameAndForeignKeys(eq("test-artifact"), eq(foreignKeys))).andReturn(entity).once();
+ expect(entity.getArtifactName()).andReturn("test-artifact").anyTimes();
+ expect(entity.getForeignKeys()).andReturn(responseForeignKeys).anyTimes();
+ expect(entity.getArtifactData()).andReturn(artifact_data).anyTimes();
+
+ // end of expectation setting
+ replay(dao, em, controller, request, clusters, cluster, entity, entity2);
+
+ // test
+ PredicateBuilder pb = new PredicateBuilder();
+ Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").and().
+ property("Artifacts/artifact_name").equals("test-artifact").end().toPredicate();
+
+ Set<Resource> response = resourceProvider.getResources(request, predicate);
+ assertEquals(1, response.size());
+ Resource resource = response.iterator().next();
+ assertEquals("test-artifact", resource.getPropertyValue("Artifacts/artifact_name"));
+ assertEquals("test-cluster", resource.getPropertyValue("Artifacts/cluster_name"));
+ assertEquals("bar", resource.getPropertyValue("artifact_data/foo"));
+ }
+
+ @Test
+ public void testGetResources_collection() throws Exception {
+ 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");
+
+ // 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(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();
+
+ // end of expectation setting
+ replay(dao, em, controller, request, clusters, cluster, entity, entity2);
+
+ // test
+ PredicateBuilder pb = new PredicateBuilder();
+ Predicate predicate = pb.begin().property("Artifacts/cluster_name").equals("test-cluster").end().toPredicate();
+
+ Set<Resource> response = resourceProvider.getResources(request, predicate);
+ assertEquals(2, response.size());
+
+ boolean artifact1Returned = false;
+ boolean artifact2Returned = false;
+ for (Resource resource : response) {
+ if (resource.getPropertyValue("Artifacts/artifact_name").equals("test-artifact")) {
+ artifact1Returned = true;
+ assertEquals("bar", resource.getPropertyValue("artifact_data/foo"));
+ assertEquals("test-cluster", resource.getPropertyValue("Artifacts/cluster_name"));
+ } else if (resource.getPropertyValue("Artifacts/artifact_name").equals("test-artifact2")) {
+ artifact2Returned = true;
+ assertEquals("bar2", resource.getPropertyValue("artifact_data/foo2"));
+ assertEquals("test-cluster", resource.getPropertyValue("Artifacts/cluster_name"));
+ } else {
+ fail("unexpected artifact name");
+ }
+ }
+ assertTrue(artifact1Returned);
+ assertTrue(artifact2Returned);
+ }
+
+ @Test
+ public void testCreateResource() throws Exception {
+ Capture<ArtifactEntity> createEntityCapture = new Capture<ArtifactEntity>();
+
+ Map<String, Object> artifact_data = Collections.<String, Object>singletonMap("foo", "bar");
+
+ TreeMap<String, String> foreignKeys = new TreeMap<String, String>();
+ foreignKeys.put("cluster", "500");
+
+ 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");
+ Set<Map<String, Object>> requestProperties = Collections.singleton(properties);
+
+ // expectations
+ expect(request.getProperties()).andReturn(requestProperties).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();
+
+ // check to see if entity already exists
+ expect(dao.findByNameAndForeignKeys(eq("test-artifact"), eq(foreignKeys))).andReturn(null).once();
+ // create
+ dao.create(capture(createEntityCapture));
+
+ // end of expectation setting
+ replay(dao, em, controller, request, clusters, cluster, entity, entity2);
+
+ resourceProvider.createResources(request);
+
+ ArtifactEntity createEntity = createEntityCapture.getValue();
+ assertEquals("test-artifact", createEntity.getArtifactName());
+ assertEquals(createEntity.getArtifactData(), artifact_data);
+ assertEquals(foreignKeys, createEntity.getForeignKeys());
+ }
+
+
+ private void setPrivateField(Object o, String field, Object value) throws Exception{
+ Class<?> c = o.getClass();
+ Field f = c.getDeclaredField(field);
+ f.setAccessible(true);
+ f.set(o, value);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/d902509f/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
index 6bb8a95..9cf016e 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
@@ -23,6 +23,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMockBuilder;
import static org.easymock.EasyMock.createNiceMock;
@@ -131,6 +132,7 @@ public class UpgradeCatalog200Test {
Capture<DBAccessor.DBColumnInfo> valueColumnCapture = new Capture<DBAccessor.DBColumnInfo>();
Capture<DBAccessor.DBColumnInfo> dataValueColumnCapture = new Capture<DBAccessor.DBColumnInfo>();
Capture<List<DBAccessor.DBColumnInfo>> alertTargetStatesCapture = new Capture<List<DBAccessor.DBColumnInfo>>();
+ Capture<List<DBAccessor.DBColumnInfo>> artifactCapture = new Capture<List<DBAccessor.DBColumnInfo>>();
Capture<List<DBAccessor.DBColumnInfo>> upgradeCapture = new Capture<List<DBAccessor.DBColumnInfo>>();
Capture<List<DBAccessor.DBColumnInfo>> upgradeGroupCapture = new Capture<List<DBAccessor.DBColumnInfo>>();
@@ -199,6 +201,9 @@ public class UpgradeCatalog200Test {
// Upgrade item
dbAccessor.createTable(eq("upgrade_item"), capture(upgradeItemCapture), eq("upgrade_item_id"));
+ // artifact
+ dbAccessor.createTable(eq("artifact"), capture(artifactCapture),
+ eq("artifact_name"), eq("foreign_keys"));
setViewInstancePropertyExpectations(dbAccessor, valueColumnCapture);
setViewInstanceDataExpectations(dbAccessor, dataValueColumnCapture);
@@ -264,6 +269,11 @@ public class UpgradeCatalog200Test {
verifyViewParameterColumns(viewparameterLabelColumnCapture, viewparameterPlaceholderColumnCapture,
viewparameterDefaultValueColumnCapture);
+ // verify artifact columns
+ List<DBAccessor.DBColumnInfo> artifactColumns = artifactCapture.getValue();
+ testCreateArtifactTable(artifactColumns);
+
+
// Verify capture group sizes
assertEquals(7, clusterVersionCapture.getValue().size());
assertEquals(4, hostVersionCapture.getValue().size());
@@ -529,4 +539,31 @@ public class UpgradeCatalog200Test {
assertNull(column.getDefaultValue());
assertTrue(column.isNullable());
}
+
+ /**
+ * assert artifact table creation
+ *
+ * @param artifactColumns artifact table columns
+ */
+ private void testCreateArtifactTable(List<DBColumnInfo> artifactColumns) {
+ assertEquals(3, artifactColumns.size());
+ for (DBColumnInfo column : artifactColumns) {
+ if (column.getName().equals("artifact_name")) {
+ assertNull(column.getDefaultValue());
+ assertEquals(String.class, column.getType());
+ assertEquals(255, (int) column.getLength());
+ assertEquals(false, column.isNullable());
+ } else if (column.getName().equals("foreign_keys")) {
+ assertNull(column.getDefaultValue());
+ assertEquals(String.class, column.getType());
+ assertEquals(false, column.isNullable());
+ } else if (column.getName().equals("artifact_data")) {
+ assertNull(column.getDefaultValue());
+ assertEquals(char[].class, column.getType());
+ assertEquals(false, column.isNullable());
+ } else {
+ fail("unexpected column name");
+ }
+ }
+ }
}