/** * 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.atlas.web.resources; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.sun.jersey.api.core.ResourceContext; import org.apache.atlas.AtlasClient; import org.apache.atlas.AtlasConstants; import org.apache.atlas.AtlasErrorCode; import org.apache.atlas.AtlasException; import org.apache.atlas.CreateUpdateEntitiesResult; import org.apache.atlas.EntityAuditEvent; import org.apache.atlas.AtlasClient.EntityResult; import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.model.instance.AtlasClassification; import org.apache.atlas.model.instance.AtlasEntity; import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo; import org.apache.atlas.model.instance.AtlasEntity.AtlasEntitiesWithExtInfo; import org.apache.atlas.model.instance.EntityMutationResponse; import org.apache.atlas.model.instance.GuidMapping; import org.apache.atlas.repository.converters.AtlasInstanceConverter; import org.apache.atlas.repository.store.graph.AtlasEntityStore; import org.apache.atlas.repository.store.graph.v1.AtlasEntityStream; import org.apache.atlas.repository.store.graph.v1.AtlasGraphUtilsV1; import org.apache.atlas.services.MetadataService; import org.apache.atlas.type.AtlasType; import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.typesystem.IStruct; import org.apache.atlas.typesystem.ITypedReferenceableInstance; import org.apache.atlas.typesystem.Referenceable; import org.apache.atlas.typesystem.exception.EntityExistsException; import org.apache.atlas.typesystem.exception.EntityNotFoundException; import org.apache.atlas.typesystem.json.InstanceSerialization; import org.apache.atlas.typesystem.persistence.Id; import org.apache.atlas.typesystem.types.ValueConversionException; import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.utils.ParamChecker; import org.apache.atlas.web.rest.EntityREST; import org.apache.atlas.web.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.codehaus.jettison.json.JSONArray; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Entity management operations as REST API. * * An entity is an "instance" of a Type. Entities conform to the definition * of the Type they correspond with. */ @Path("entities") @Singleton @Deprecated public class EntityResource { private static final Logger LOG = LoggerFactory.getLogger(EntityResource.class); private static final Logger PERF_LOG = AtlasPerfTracer.getPerfLogger("rest.EntityResource"); private static final String TRAIT_NAME = "traitName"; private final MetadataService metadataService; private final AtlasInstanceConverter restAdapters; private final AtlasEntityStore entitiesStore; private final AtlasTypeRegistry typeRegistry; @Context UriInfo uriInfo; @Context private ResourceContext resourceContext; /** * Created by the Guice ServletModule and injected with the * configured MetadataService. * * @param metadataService metadata service handle */ @Inject public EntityResource(MetadataService metadataService, AtlasInstanceConverter restAdapters, AtlasEntityStore entitiesStore, AtlasTypeRegistry typeRegistry) { this.metadataService = metadataService; this.restAdapters = restAdapters; this.entitiesStore = entitiesStore; this.typeRegistry = typeRegistry; } /** * Submits the entity definitions (instances). * The body contains the JSONArray of entity json. The service takes care of de-duping the entities based on any * unique attribute for the give type. */ @POST @Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) @Produces(Servlets.JSON_MEDIA_TYPE) public Response submit(@Context HttpServletRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.submit()"); } String entityJson = null; AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.submit()"); } String entities = Servlets.getRequestPayload(request); //Handle backward compatibility - if entities is not JSONArray, convert to JSONArray try { new JSONArray(entities); } catch (JSONException e) { final String finalEntities = entities; entities = new JSONArray() {{ put(finalEntities); }}.toString(); } entityJson = AtlasClient.toString(new JSONArray(entities)); if (LOG.isDebugEnabled()) { LOG.debug("submitting entities {} ", entityJson); } EntityREST entityREST = resourceContext.getResource(EntityREST.class); AtlasEntitiesWithExtInfo entitiesInfo = restAdapters.toAtlasEntities(entities); EntityMutationResponse mutationResponse = entityREST.createOrUpdate(entitiesInfo); final List<String> guids = restAdapters.getGuids(mutationResponse.getCreatedEntities()); if (LOG.isDebugEnabled()) { LOG.debug("Created entities {}", guids); } final CreateUpdateEntitiesResult result = restAdapters.toCreateUpdateEntitiesResult(mutationResponse); JSONObject response = getResponse(result); URI locationURI = getLocationURI(guids); return Response.created(locationURI).entity(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to persist entity instance entityDef={}", entityJson, e); throw toWebApplicationException(e); } catch(EntityExistsException e) { LOG.error("Unique constraint violation for entity entityDef={}", entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.CONFLICT)); } catch (ValueConversionException ve) { LOG.error("Unable to persist entity instance due to a deserialization error entityDef={}", entityJson, ve); throw new WebApplicationException(Servlets.getErrorResponse(ve.getCause() != null ? ve.getCause() : ve, Response.Status.BAD_REQUEST)); } catch (AtlasException | IllegalArgumentException e) { LOG.error("Unable to persist entity instance entityDef={}", entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to persist entity instance entityDef={}", entityJson, e); throw e; } catch (Throwable e) { LOG.error("Unable to persist entity instance entityDef={}", entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.submit()"); } } } @VisibleForTesting public URI getLocationURI(List<String> guids) { URI locationURI = null; if (uriInfo != null) { UriBuilder ub = uriInfo.getAbsolutePathBuilder(); locationURI = CollectionUtils.isEmpty(guids) ? null : ub.path(guids.get(0)).build(); } else { String uriPath = AtlasClient.API.GET_ENTITY.getPath(); locationURI = guids.isEmpty() ? null : UriBuilder .fromPath(AtlasConstants.DEFAULT_ATLAS_REST_ADDRESS) .path(uriPath).path(guids.get(0)).build(); } return locationURI; } private JSONObject getResponse(AtlasClient.EntityResult entityResult) throws AtlasException, JSONException { CreateUpdateEntitiesResult result = new CreateUpdateEntitiesResult(); result.setEntityResult(entityResult); return getResponse(result); } private JSONObject getResponse(CreateUpdateEntitiesResult result) throws AtlasException, JSONException { JSONObject response = new JSONObject(); EntityResult entityResult = result.getEntityResult(); GuidMapping mapping = result.getGuidMapping(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); if(entityResult != null) { response.put(AtlasClient.ENTITIES, new JSONObject(entityResult.toString()).get(AtlasClient.ENTITIES)); String sampleEntityId = getSample(result.getEntityResult()); if (sampleEntityId != null) { String entityDefinition = metadataService.getEntityDefinitionJson(sampleEntityId); response.put(AtlasClient.DEFINITION, new JSONObject(entityDefinition)); } } if(mapping != null) { response.put(AtlasClient.GUID_ASSIGNMENTS, new JSONObject(AtlasType.toJson(mapping)).get(AtlasClient.GUID_ASSIGNMENTS)); } return response; } /** * Complete update of a set of entities - the values not specified will be replaced with null/removed * Adds/Updates given entities identified by its GUID or unique attribute * @return response payload as json */ @PUT @Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) @Produces(Servlets.JSON_MEDIA_TYPE) public Response updateEntities(@Context HttpServletRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.updateEntities()"); } String entityJson = null; AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.updateEntities()"); } final String entities = Servlets.getRequestPayload(request); entityJson = AtlasClient.toString(new JSONArray(entities)); if (LOG.isDebugEnabled()) { LOG.info("updating entities {} ", entityJson); } EntityREST entityREST = resourceContext.getResource(EntityREST.class); AtlasEntitiesWithExtInfo entitiesInfo = restAdapters.toAtlasEntities(entities); EntityMutationResponse mutationResponse = entityREST.createOrUpdate(entitiesInfo); CreateUpdateEntitiesResult result = restAdapters.toCreateUpdateEntitiesResult(mutationResponse); if (LOG.isDebugEnabled()) { LOG.debug("Updated entities: {}", result.getEntityResult()); } JSONObject response = getResponse(result); return Response.ok(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to persist entity instance entityDef={}", entityJson, e); throw toWebApplicationException(e); } catch(EntityExistsException e) { LOG.error("Unique constraint violation for entityDef={}", entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.CONFLICT)); } catch (ValueConversionException ve) { LOG.error("Unable to persist entity instance due to a deserialization error entityDef={}", entityJson, ve); throw new WebApplicationException(Servlets.getErrorResponse(ve.getCause(), Response.Status.BAD_REQUEST)); } catch (AtlasException | IllegalArgumentException e) { LOG.error("Unable to persist entity instance entityDef={}", entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to persist entity instance entityDef={}", entityJson, e); throw e; } catch (Throwable e) { LOG.error("Unable to persist entity instance entityDef={}", entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.updateEntities()"); } } } private String getSample(AtlasClient.EntityResult entityResult) { String sample = getSample(entityResult.getCreatedEntities()); if (sample == null) { sample = getSample(entityResult.getUpdateEntities()); } return sample; } private String getSample(List<String> list) { if (list != null && list.size() > 0) { return list.get(0); } return null; } /** * Adds/Updates given entity identified by its unique attribute( entityType, attributeName and value) * Updates support only partial update of an entity - Adds/updates any new values specified * Updates do not support removal of attribute values * * @param entityType the entity type * @param attribute the unique attribute used to identify the entity * @param value the unique attributes value * @param request The updated entity json * @return response payload as json * The body contains the JSONArray of entity json. The service takes care of de-duping the entities based on any * unique attribute for the give type. */ @POST @Path("qualifiedName") @Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) @Produces(Servlets.JSON_MEDIA_TYPE) public Response updateByUniqueAttribute(@QueryParam("type") String entityType, @QueryParam("property") String attribute, @QueryParam("value") String value, @Context HttpServletRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.updateByUniqueAttribute({}, {}, {})", entityType, attribute, value); } AtlasPerfTracer perf = null; String entityJson = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.updateByUniqueAttribute(" + entityType + ", " + attribute + ", " + value + ")"); } entityJson = Servlets.getRequestPayload(request); if (LOG.isDebugEnabled()) { LOG.debug("Partially updating entity by unique attribute {} {} {} {} ", entityType, attribute, value, entityJson); } Referenceable updatedEntity = InstanceSerialization.fromJsonReferenceable(entityJson, true); entityType = ParamChecker.notEmpty(entityType, "Entity type cannot be null"); attribute = ParamChecker.notEmpty(attribute, "attribute name cannot be null"); value = ParamChecker.notEmpty(value, "attribute value cannot be null"); Map<String, Object> attributes = new HashMap<>(); attributes.put(attribute, value); // update referenceable with Id if not specified in payload Id updateId = updatedEntity.getId(); if (updateId != null && !updateId.isAssigned()) { String guid = AtlasGraphUtilsV1.getGuidByUniqueAttributes(getEntityType(entityType), attributes); updatedEntity.replaceWithNewId(new Id(guid, 0, updatedEntity.getTypeName())); } AtlasEntitiesWithExtInfo entitiesInfo = restAdapters.toAtlasEntity(updatedEntity); EntityMutationResponse mutationResponse = entitiesStore.createOrUpdate(new AtlasEntityStream(entitiesInfo), true); CreateUpdateEntitiesResult result = restAdapters.toCreateUpdateEntitiesResult(mutationResponse); if (LOG.isDebugEnabled()) { LOG.debug("Updated entities: {}", result.getEntityResult()); } JSONObject response = getResponse(result); return Response.ok(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to partially update entity {} {}:{}.{}", entityJson, entityType, attribute, value, e); throw toWebApplicationException(e); } catch (ValueConversionException ve) { LOG.error("Unable to persist entity instance due to a deserialization error {} ", entityJson, ve); throw new WebApplicationException(Servlets.getErrorResponse(ve.getCause(), Response.Status.BAD_REQUEST)); } catch(EntityExistsException e) { LOG.error("Unique constraint violation for entity {} ", entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.CONFLICT)); } catch (EntityNotFoundException e) { LOG.error("An entity with type={} and qualifiedName={} does not exist {} ", entityType, value, entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.NOT_FOUND)); } catch (AtlasException | IllegalArgumentException e) { LOG.error("Unable to partially update entity {} {}:{}.{}", entityJson, entityType, attribute, value, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to partially update entity {} {}:{}.{}", entityJson, entityType, attribute, value, e); throw e; } catch (Throwable e) { LOG.error("Unable to partially update entity {} {}:{}.{}", entityJson, entityType, attribute, value, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.updateByUniqueAttribute({}, {}, {})", entityType, attribute, value); } } } /** * Updates entity identified by its GUID * Support Partial update of an entity - Adds/updates any new values specified * Does not support removal of attribute values * * @param guid * @param request The updated entity json * @return */ @POST @Path("{guid}") @Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) @Produces(Servlets.JSON_MEDIA_TYPE) public Response updateEntityByGuid(@PathParam("guid") String guid, @QueryParam("property") String attribute, @Context HttpServletRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.updateEntityByGuid({}, {})", guid, attribute); } AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.updateEntityByGuid(" + guid + ", " + attribute + ")"); } if (StringUtils.isEmpty(attribute)) { return partialUpdateEntityByGuid(guid, request); } else { return partialUpdateEntityAttrByGuid(guid, attribute, request); } } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.updateEntityByGuid({}, {})", guid, attribute); } } } private Response partialUpdateEntityByGuid(String guid, HttpServletRequest request) { String entityJson = null; try { guid = ParamChecker.notEmpty(guid, "Guid property cannot be null"); entityJson = Servlets.getRequestPayload(request); if (LOG.isDebugEnabled()) { LOG.debug("partially updating entity for guid {} : {} ", guid, entityJson); } Referenceable updatedEntity = InstanceSerialization.fromJsonReferenceable(entityJson, true); // update referenceable with Id if not specified in payload Id updateId = updatedEntity.getId(); if (updateId != null && !updateId.isAssigned()) { updatedEntity.replaceWithNewId(new Id(guid, 0, updatedEntity.getTypeName())); } AtlasEntitiesWithExtInfo entitiesInfo = restAdapters.toAtlasEntity(updatedEntity); EntityMutationResponse mutationResponse = entitiesStore.createOrUpdate(new AtlasEntityStream(entitiesInfo), true); CreateUpdateEntitiesResult result = restAdapters.toCreateUpdateEntitiesResult(mutationResponse); if (LOG.isDebugEnabled()) { LOG.debug("Updated entities: {}", result.getEntityResult()); } JSONObject response = getResponse(result); return Response.ok(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to update entity by GUID {} {} ", guid, entityJson, e); throw toWebApplicationException(e); } catch (EntityNotFoundException e) { LOG.error("An entity with GUID={} does not exist {} ", guid, entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.NOT_FOUND)); } catch (AtlasException | IllegalArgumentException e) { LOG.error("Unable to update entity by GUID {} {}", guid, entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to update entity by GUID {} {} ", guid, entityJson, e); throw e; } catch (Throwable e) { LOG.error("Unable to update entity by GUID {} {} ", guid, entityJson, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } } /** * Supports Partial updates * Adds/Updates given entity specified by its GUID * Supports updation of only simple primitive attributes like strings, ints, floats, enums, class references and * does not support updation of complex types like arrays, maps * @param guid entity id * @param property property to add * @postbody property's value * @return response payload as json */ private Response partialUpdateEntityAttrByGuid(String guid, String property, HttpServletRequest request) { String value = null; try { Preconditions.checkNotNull(property, "Entity property cannot be null"); value = Servlets.getRequestPayload(request); Preconditions.checkNotNull(value, "Entity value cannot be null"); if (LOG.isDebugEnabled()) { LOG.debug("Updating entity {} for property {} = {}", guid, property, value); } EntityMutationResponse mutationResponse = entitiesStore.updateEntityAttributeByGuid(guid, property, value); CreateUpdateEntitiesResult result = restAdapters.toCreateUpdateEntitiesResult(mutationResponse); if (LOG.isDebugEnabled()) { LOG.debug("Updated entities: {}", result.getEntityResult()); } JSONObject response = getResponse(result); return Response.ok(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to add property {} to entity id {} {} ", property, guid, value, e); throw toWebApplicationException(e); } catch (EntityNotFoundException e) { LOG.error("An entity with GUID={} does not exist {} ", guid, value, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.NOT_FOUND)); } catch (AtlasException | IllegalArgumentException e) { LOG.error("Unable to add property {} to entity id {} {} ", property, guid, value, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to add property {} to entity id {} {} ", property, guid, value, e); throw e; } catch (Throwable e) { LOG.error("Unable to add property {} to entity id {} {} ", property, guid, value, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } } /** * Delete entities from the repository identified by their guids (including their composite references) * or * Deletes a single entity identified by its type and unique attribute value from the repository (including their composite references) * * @param guids list of deletion candidate guids * or * @param entityType the entity type * @param attribute the unique attribute used to identify the entity * @param value the unique attribute value used to identify the entity * @return response payload as json - including guids of entities(including composite references from that entity) that were deleted */ @DELETE @Produces(Servlets.JSON_MEDIA_TYPE) public Response deleteEntities(@QueryParam("guid") List<String> guids, @QueryParam("type") String entityType, @QueryParam("property") final String attribute, @QueryParam("value") final String value) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.deleteEntities({}, {}, {}, {})", guids, entityType, attribute, value); } AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.deleteEntities(" + guids + ", " + entityType + ", " + attribute + ", " + value + ")"); } EntityResult entityResult; EntityREST entityREST = resourceContext.getResource(EntityREST.class); if (guids != null && !guids.isEmpty()) { if (LOG.isDebugEnabled()) { LOG.debug("Deleting entities {}", guids); } EntityMutationResponse mutationResponse = entityREST.deleteByGuids(guids); entityResult = restAdapters.toCreateUpdateEntitiesResult(mutationResponse).getEntityResult(); } else { if (LOG.isDebugEnabled()) { LOG.debug("Deleting entity type={} with property {}={}", entityType, attribute, value); } Map<String, Object> attributes = new HashMap<>(); attributes.put(attribute, value); EntityMutationResponse mutationResponse = entitiesStore.deleteByUniqueAttributes(getEntityType(entityType), attributes); entityResult = restAdapters.toCreateUpdateEntitiesResult(mutationResponse).getEntityResult(); } if (LOG.isDebugEnabled()) { LOG.debug("Deleted entity result: {}", entityResult); } JSONObject response = getResponse(entityResult); return Response.ok(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to delete entities {} {} {} {} ", guids, entityType, attribute, value, e); throw toWebApplicationException(e); } catch (EntityNotFoundException e) { if(guids != null && !guids.isEmpty()) { LOG.error("An entity with GUID={} does not exist ", guids, e); } else { LOG.error("An entity with qualifiedName {}-{}-{} does not exist", entityType, attribute, value, e); } throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.NOT_FOUND)); } catch (AtlasException | IllegalArgumentException e) { LOG.error("Unable to delete entities {} {} {} {} ", guids, entityType, attribute, value, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to delete entities {} {} {} {} ", guids, entityType, attribute, value, e); throw e; } catch (Throwable e) { LOG.error("Unable to delete entities {} {} {} {} ", guids, entityType, attribute, value, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.deleteEntities({}, {}, {}, {})", guids, entityType, attribute, value); } } } /** * Fetch the complete definition of an entity given its GUID. * * @param guid GUID for the entity */ @GET @Path("{guid}") @Produces(Servlets.JSON_MEDIA_TYPE) public Response getEntityDefinition(@PathParam("guid") String guid) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.getEntityDefinition({})", guid); } AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.getEntityDefinition(" + guid + ")"); } if (LOG.isDebugEnabled()) { LOG.debug("Fetching entity definition for guid={} ", guid); } guid = ParamChecker.notEmpty(guid, "guid cannot be null"); final String entityDefinition = metadataService.getEntityDefinitionJson(guid); JSONObject response = new JSONObject(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); Response.Status status = Response.Status.NOT_FOUND; if (entityDefinition != null) { response.put(AtlasClient.DEFINITION, new JSONObject(entityDefinition)); status = Response.Status.OK; } else { response.put(AtlasClient.ERROR, Servlets.escapeJsonString(String.format("An entity with GUID={%s} does not exist", guid))); } return Response.status(status).entity(response).build(); } catch (EntityNotFoundException e) { LOG.error("An entity with GUID={} does not exist ", guid, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.NOT_FOUND)); } catch (AtlasException | IllegalArgumentException e) { LOG.error("Bad GUID={} ", guid, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to get instance definition for GUID {}", guid, e); throw e; } catch (Throwable e) { LOG.error("Unable to get instance definition for GUID {}", guid, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.getEntityDefinition({})", guid); } } } /** * Gets the list of entities for a given entity type. * * @param entityType name of a type which is unique */ public Response getEntityListByType(String entityType) { try { Preconditions.checkNotNull(entityType, "Entity type cannot be null"); if (LOG.isDebugEnabled()) { LOG.debug("Fetching entity list for type={} ", entityType); } final List<String> entityList = metadataService.getEntityList(entityType); JSONObject response = new JSONObject(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(AtlasClient.TYPENAME, entityType); response.put(AtlasClient.RESULTS, new JSONArray(entityList)); response.put(AtlasClient.COUNT, entityList.size()); return Response.ok(response).build(); } catch (NullPointerException e) { LOG.error("Entity type cannot be null", e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (AtlasException | IllegalArgumentException e) { LOG.error("Unable to get entity list for type {}", entityType, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to get entity list for type {}", entityType, e); throw e; } catch (Throwable e) { LOG.error("Unable to get entity list for type {}", entityType, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } } @GET @Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) @Produces(Servlets.JSON_MEDIA_TYPE) public Response getEntity(@QueryParam("type") String entityType, @QueryParam("property") String attribute, @QueryParam("value") final String value) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.getEntity({}, {}, {})", entityType, attribute, value); } AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.getEntity(" + entityType + ", " + attribute + ", " + value + ")"); } if (StringUtils.isEmpty(attribute)) { //List API return getEntityListByType(entityType); } else { //Get entity by unique attribute return getEntityDefinitionByAttribute(entityType, attribute, value); } } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.getEntity({}, {}, {})", entityType, attribute, value); } } } /** * Fetch the complete definition of an entity given its qualified name. * * @param entityType * @param attribute * @param value */ public Response getEntityDefinitionByAttribute(String entityType, String attribute, String value) { try { if (LOG.isDebugEnabled()) { LOG.debug("Fetching entity definition for type={}, qualified name={}", entityType, value); } entityType = ParamChecker.notEmpty(entityType, "Entity type cannot be null"); attribute = ParamChecker.notEmpty(attribute, "attribute name cannot be null"); value = ParamChecker.notEmpty(value, "attribute value cannot be null"); Map<String, Object> attributes = new HashMap<>(); attributes.put(attribute, value); AtlasEntityWithExtInfo entityInfo; try { entityInfo = entitiesStore.getByUniqueAttributes(getEntityType(entityType), attributes); } catch (AtlasBaseException e) { LOG.error("Cannot find entity with type: {}, attribute: {} and value: {}", entityType, attribute, value); throw toWebApplicationException(e); } String entityDefinition = null; if (entityInfo != null) { AtlasEntity entity = entityInfo.getEntity(); final ITypedReferenceableInstance instance = restAdapters.getITypedReferenceable(entity); entityDefinition = InstanceSerialization.toJson(instance, true); } JSONObject response = new JSONObject(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); Response.Status status = Response.Status.NOT_FOUND; if (entityDefinition != null) { response.put(AtlasClient.DEFINITION, new JSONObject(entityDefinition)); status = Response.Status.OK; } else { response.put(AtlasClient.ERROR, Servlets.escapeJsonString(String.format("An entity with type={%s}, " + "qualifiedName={%s} does not exist", entityType, value))); } return Response.status(status).entity(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to get instance definition for type={}, qualifiedName={}", entityType, value, e); throw toWebApplicationException(e); } catch (IllegalArgumentException e) { LOG.error("Bad type={}, qualifiedName={}", entityType, value, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to get instance definition for type={}, qualifiedName={}", entityType, value, e); throw e; } catch (Throwable e) { LOG.error("Unable to get instance definition for type={}, qualifiedName={}", entityType, value, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } } // Trait management functions /** * Gets the list of trait names for a given entity represented by a guid. * * @param guid globally unique identifier for the entity * @return a list of trait names for the given entity guid */ @GET @Path("{guid}/traits") @Produces(Servlets.JSON_MEDIA_TYPE) public Response getTraitNames(@PathParam("guid") String guid) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.getTraitNames({})", guid); } AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.getTraitNames(" + guid + ")"); } if (LOG.isDebugEnabled()) { LOG.debug("Fetching trait names for entity={}", guid); } final List<AtlasClassification> classifications = entitiesStore.getClassifications(guid); List<String> traitNames = new ArrayList<>(); for (AtlasClassification classification : classifications) { traitNames.add(classification.getTypeName()); } JSONObject response = new JSONObject(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(AtlasClient.RESULTS, new JSONArray(traitNames)); response.put(AtlasClient.COUNT, traitNames.size()); return Response.ok(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to get trait definition for entity {}", guid, e); throw toWebApplicationException(e); } catch (IllegalArgumentException e) { LOG.error("Unable to get trait definition for entity {}", guid, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to get trait names for entity {}", guid, e); throw e; } catch (Throwable e) { LOG.error("Unable to get trait names for entity {}", guid, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.getTraitNames({})", guid); } } } /** * Fetches the trait definitions of all the traits associated to the given entity * @param guid globally unique identifier for the entity */ @GET @Path("{guid}/traitDefinitions") @Produces(Servlets.JSON_MEDIA_TYPE) public Response getTraitDefinitionsForEntity(@PathParam("guid") String guid){ if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.getTraitDefinitionsForEntity({})", guid); } AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.getTraitDefinitionsForEntity(" + guid + ")"); } if (LOG.isDebugEnabled()) { LOG.debug("Fetching all trait definitions for entity={}", guid); } final List<AtlasClassification> classifications = entitiesStore.getClassifications(guid); JSONArray traits = new JSONArray(); for (AtlasClassification classification : classifications) { IStruct trait = restAdapters.getTrait(classification); traits.put(new JSONObject(InstanceSerialization.toJson(trait, true))); } JSONObject response = new JSONObject(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(AtlasClient.RESULTS, traits); response.put(AtlasClient.COUNT, traits.length()); return Response.ok(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to get trait definition for entity {}", guid, e); throw toWebApplicationException(e); } catch (IllegalArgumentException e) { LOG.error("Unable to get trait definition for entity {}", guid, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to get trait definitions for entity {}", guid, e); throw e; } catch (Throwable e) { LOG.error("Unable to get trait definitions for entity {}", guid, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.getTraitDefinitionsForEntity({})", guid); } } } /** * Fetches the trait definition for an entity given its guid and trait name * * @param guid globally unique identifier for the entity * @param traitName name of the trait */ @GET @Path("{guid}/traitDefinitions/{traitName}") @Produces(Servlets.JSON_MEDIA_TYPE) public Response getTraitDefinitionForEntity(@PathParam("guid") String guid, @PathParam("traitName") String traitName){ if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.getTraitDefinitionForEntity({}, {})", guid, traitName); } AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.getTraitDefinitionForEntity(" + guid + ", " + traitName + ")"); } if (LOG.isDebugEnabled()) { LOG.debug("Fetching trait definition for entity {} and trait name {}", guid, traitName); } final AtlasClassification classification = entitiesStore.getClassification(guid, traitName); IStruct traitDefinition = restAdapters.getTrait(classification); JSONObject response = new JSONObject(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(AtlasClient.RESULTS, new JSONObject(InstanceSerialization.toJson(traitDefinition, true))); return Response.ok(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to get trait definition for entity {} and trait {}", guid, traitName, e); throw toWebApplicationException(e); } catch (IllegalArgumentException e) { LOG.error("Unable to get trait definition for entity {} and trait {}", guid, traitName, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to get trait definition for entity {} and trait {}", guid, traitName, e); throw e; } catch (Throwable e) { LOG.error("Unable to get trait definition for entity {} and trait {}", guid, traitName, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.getTraitDefinitionForEntity({}, {})", guid, traitName); } } } /** * Adds a new trait to an existing entity represented by a guid. * * @param guid globally unique identifier for the entity */ @POST @Path("{guid}/traits") @Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) @Produces(Servlets.JSON_MEDIA_TYPE) public Response addTrait(@Context HttpServletRequest request, @PathParam("guid") final String guid) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.addTrait({})", guid); } String traitDefinition = null; AtlasPerfTracer perf = null; try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.addTrait(" + guid + ")"); } traitDefinition = Servlets.getRequestPayload(request); if (LOG.isDebugEnabled()) { LOG.debug("Adding trait={} for entity={} ", traitDefinition, guid); } List<String> guids = new ArrayList<String>() {{ add(guid); }}; entitiesStore.addClassification(guids, restAdapters.getClassification(InstanceSerialization.fromJsonStruct(traitDefinition, true))); URI locationURI = getLocationURI(new ArrayList<String>() {{ add(guid); }}); JSONObject response = new JSONObject(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); return Response.created(locationURI).entity(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to add trait for entity={} traitDef={}", guid, traitDefinition, e); throw toWebApplicationException(e); } catch (IllegalArgumentException e) { LOG.error("Unable to add trait for entity={} traitDef={}", guid, traitDefinition, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to add trait for entity={} traitDef={}", guid, traitDefinition, e); throw e; } catch (Throwable e) { LOG.error("Unable to add trait for entity={} traitDef={}", guid, traitDefinition, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.addTrait({})", guid); } } } /** * Deletes a given trait from an existing entity represented by a guid. * * @param guid globally unique identifier for the entity * @param traitName name of the trait */ @DELETE @Path("{guid}/traits/{traitName}") @Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) @Produces(Servlets.JSON_MEDIA_TYPE) public Response deleteTrait(@Context HttpServletRequest request, @PathParam("guid") String guid, @PathParam(TRAIT_NAME) final String traitName) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.deleteTrait({}, {})", guid, traitName); } AtlasPerfTracer perf = null; if (LOG.isDebugEnabled()) { LOG.debug("Deleting trait={} from entity={} ", traitName, guid); } try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.deleteTrait(" + guid + ", " + traitName + ")"); } entitiesStore.deleteClassifications(guid, new ArrayList<String>() {{ add(traitName); }}); JSONObject response = new JSONObject(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(TRAIT_NAME, traitName); return Response.ok(response).build(); } catch (AtlasBaseException e) { LOG.error("Unable to delete trait name={} for entity={}", traitName, guid, e); throw toWebApplicationException(e); } catch (IllegalArgumentException e) { LOG.error("Unable to delete trait name={} for entity={}", traitName, guid, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to delete trait name={} for entity={}", traitName, guid, e); throw e; } catch (Throwable e) { LOG.error("Unable to delete trait name={} for entity={}", traitName, guid, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.deleteTrait({}, {})", guid, traitName); } } } /** * Returns the entity audit events for a given entity id. The events are returned in the decreasing order of timestamp. * @param guid entity id * @param startKey used for pagination. Startkey is inclusive, the returned results contain the event with the given startkey. * First time getAuditEvents() is called for an entity, startKey should be null, * with count = (number of events required + 1). Next time getAuditEvents() is called for the same entity, * startKey should be equal to the entityKey of the last event returned in the previous call. * @param count number of events required * @return */ @GET @Path("{guid}/audit") @Produces(Servlets.JSON_MEDIA_TYPE) public Response getAuditEvents(@PathParam("guid") String guid, @QueryParam("startKey") String startKey, @QueryParam("count") @DefaultValue("100") short count) { if (LOG.isDebugEnabled()) { LOG.debug("==> EntityResource.getAuditEvents({}, {}, {})", guid, startKey, count); } AtlasPerfTracer perf = null; if (LOG.isDebugEnabled()) { LOG.debug("Audit events request for entity {}, start key {}, number of results required {}", guid, startKey, count); } try { if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) { perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityResource.getAuditEvents(" + guid + ", " + startKey + ", " + count + ")"); } List<EntityAuditEvent> events = metadataService.getAuditEvents(guid, startKey, count); JSONObject response = new JSONObject(); response.put(AtlasClient.REQUEST_ID, Servlets.getRequestId()); response.put(AtlasClient.EVENTS, getJSONArray(events)); return Response.ok(response).build(); } catch (AtlasException | IllegalArgumentException e) { LOG.error("Unable to get audit events for entity guid={} startKey={}", guid, startKey, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } catch (WebApplicationException e) { LOG.error("Unable to get audit events for entity guid={} startKey={}", guid, startKey, e); throw e; } catch (Throwable e) { LOG.error("Unable to get audit events for entity guid={} startKey={}", guid, startKey, e); throw new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.INTERNAL_SERVER_ERROR)); } finally { AtlasPerfTracer.log(perf); if (LOG.isDebugEnabled()) { LOG.debug("<== EntityResource.getAuditEvents({}, {}, {})", guid, startKey, count); } } } private <T> JSONArray getJSONArray(Collection<T> elements) throws JSONException { JSONArray jsonArray = new JSONArray(); for(T element : elements) { jsonArray.put(new JSONObject(element.toString())); } return jsonArray; } private AtlasEntityType getEntityType(String typeName) throws AtlasBaseException { AtlasEntityType ret = typeRegistry.getEntityTypeByName(typeName); if (ret == null) { throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_NOT_FOUND, typeName); } return ret; } public static WebApplicationException toWebApplicationException(AtlasBaseException e) { if (e.getAtlasErrorCode() == AtlasErrorCode.CLASSIFICATION_NOT_FOUND || e.getAtlasErrorCode() == AtlasErrorCode.INSTANCE_GUID_NOT_FOUND || e.getAtlasErrorCode() == AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND) { return new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.NOT_FOUND)); } if (e.getAtlasErrorCode() == AtlasErrorCode.INVALID_PARAMETERS || e.getAtlasErrorCode() == AtlasErrorCode.INSTANCE_CRUD_INVALID_PARAMS) { return new WebApplicationException(Servlets.getErrorResponse(e, Response.Status.BAD_REQUEST)); } return new WebApplicationException(Servlets.getErrorResponse(e, e.getAtlasErrorCode().getHttpCode())); } }