Commit 9cb56c54 by ashutoshm Committed by Madhan Neethiraj

ATLAS-1503: implementation of REST API for export

parent b63d6316
......@@ -59,7 +59,8 @@ public enum AtlasErrorCode {
CONSTRAINT_INVERSE_REF_INVERSE_ATTRIBUTE_NON_EXISTING(400, "ATLAS40028E", "{0}.{1}: invalid {2} constraint. Inverse attribute {3}.{4} does not exist"),
CONSTRAINT_INVERSE_REF_INVERSE_ATTRIBUTE_INVALID_TYPE(400, "ATLAS40029E", "{0}.{1}: invalid {2} constraint. Inverse attribute {3}.{4} is not an entity type"),
CONSTRAINT_OWNED_REF_ATTRIBUTE_INVALID_TYPE(400, "ATLAS40030E", "{0}.{1}: invalid {2} constraint. Attribute {3} is not an entity type"),
CANNOT_MAP_ATTRIBUTE(400, "ATLAS40027E", "cannot map attribute: {0} of type: {1} from vertex"),
CANNOT_MAP_ATTRIBUTE(400, "ATLAS40031E", "cannot map attribute: {0} of type: {1} from vertex"),
INVALID_OBJECT_ID(400, "ATLAS40032E", "ObjectId is not valid {0}"),
// All Not found enums go here
TYPE_NAME_NOT_FOUND(404, "ATLAS4041E", "Given typename {0} was invalid"),
......
/**
* 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.model.impexp;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.atlas.model.instance.AtlasObjectId;
import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONLY;
@JsonAutoDetect(getterVisibility=PUBLIC_ONLY, setterVisibility=PUBLIC_ONLY, fieldVisibility=NONE)
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown=true)
@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
public class AtlasExportRequest implements Serializable {
private static final long serialVersionUID = 1L;
public static final String EXPORT_PARAM_OPTION = "FORMAT";
public static final String EXPORT_PARAM_OPTION_FORMAT_JSON = "JSON";
public static final String EXPORT_PARAM_OPTION_FORMAT_ZIP = "ZIP";
private List<AtlasObjectId> itemsToExport = new ArrayList<>();
private Map<String, Object> options = new HashMap<>();
public List<AtlasObjectId> getItemsToExport() {
return itemsToExport;
}
public void setItemsToExport(List<AtlasObjectId> itemsToExport) {
this.itemsToExport = itemsToExport;
}
public Map<String, Object> getOptions() {
return options;
}
public void setOptions(Map<String, Object> options) {
this.options = options;
}
public StringBuilder toString(StringBuilder sb) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append("AtlasExportRequest{");
sb.append("itemsToExport={");
AtlasBaseTypeDef.dumpObjects(itemsToExport, sb);
sb.append("}");
sb.append("options={");
AtlasBaseTypeDef.dumpObjects(options, sb);
sb.append("}");
sb.append("}");
return sb;
}
@Override
public String toString() {
return toString(new StringBuilder()).toString();
}
}
/**
* 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.model.impexp;
import org.apache.atlas.model.instance.AtlasEntity;
import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
import org.apache.atlas.model.typedef.AtlasTypesDef;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.PUBLIC_ONLY;
@JsonAutoDetect(getterVisibility=PUBLIC_ONLY, setterVisibility=PUBLIC_ONLY, fieldVisibility=NONE)
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown=true)
@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
public class AtlasExportResult implements Serializable {
private static final long serialVersionUID = 1L;
public final static String ENTITY_COUNT = "entityCount";
public enum OperationStatus {
SUCCESS, PARTIAL_SUCCESS, FAIL
}
private AtlasExportRequest request;
private String userName;
private String clientIpAddress;
private String hostName;
private long timeStamp;
private Map<String, Integer> metrics;
private AtlasExportData data;
private OperationStatus operationStatus;
public AtlasExportResult() {
this(null, null, null, null, System.currentTimeMillis());
}
public AtlasExportResult(AtlasExportRequest request,
String userName, String clientIpAddress, String hostName, long timeStamp) {
this.request = request;
this.userName = userName;
this.clientIpAddress = clientIpAddress;
this.hostName = hostName;
this.timeStamp = timeStamp;
this.metrics = new HashMap<>();
this.operationStatus = OperationStatus.FAIL;
this.data = new AtlasExportData();
}
public AtlasExportRequest getRequest() {
return request;
}
public void setRequest(AtlasExportRequest request) {
this.request = request;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getClientIpAddress() {
return clientIpAddress;
}
public void setClientIpAddress(String clientIpAddress) {
this.clientIpAddress = clientIpAddress;
}
public String getHostName() {
return hostName;
}
public void setHostName(String hostName) {
this.hostName = hostName;
}
public long getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(long timeStamp) {
this.timeStamp = timeStamp;
}
public Map<String, Integer> getMetrics() {
return metrics;
}
public void setMetrics(Map<String, Integer> metrics) {
this.metrics = metrics;
}
public AtlasExportData getData() {
return data;
}
public void setData(AtlasExportData data) {
this.data = data;
}
public OperationStatus getOperationStatus() {
return operationStatus;
}
public void setOperationStatus(OperationStatus operationStatus) {
this.operationStatus = operationStatus;
}
public void incrementMeticsCounter(String key) {
incrementMeticsCounter(key, 1);
}
public void incrementMeticsCounter(String key, int incrementBy) {
int currentValue = metrics.containsKey(key) ? metrics.get(key) : 0;
metrics.put(key, currentValue + incrementBy);
}
public StringBuilder toString(StringBuilder sb) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append("AtlasExportResult{");
sb.append("request={").append(request).append("}");
sb.append(", userName='").append(userName).append("'");
sb.append(", clientIpAddress='").append(clientIpAddress).append("'");
sb.append(", hostName='").append(hostName).append("'");
sb.append(", timeStamp='").append(timeStamp).append("'");
sb.append(", metrics={");
AtlasBaseTypeDef.dumpObjects(metrics, sb);
sb.append("}");
sb.append(", data='").append(data).append("'");
sb.append(", operationStatus='").append(operationStatus).append("'");
sb.append("}");
return sb;
}
@Override
public String toString() {
return toString(new StringBuilder()).toString();
}
@JsonAutoDetect(getterVisibility=PUBLIC_ONLY, setterVisibility=PUBLIC_ONLY, fieldVisibility=NONE)
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown=true)
@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
public static class AtlasExportData implements Serializable{
private static final long serialVersionUID = 1L;
private AtlasTypesDef typesDef;
private Map<String, AtlasEntity> entities;
private Map<String, List<String>> entityCreationOrder;
public AtlasExportData() {
typesDef = new AtlasTypesDef();
entities = new HashMap<>();
entityCreationOrder = new HashMap<>();
}
public AtlasTypesDef getTypesDef() { return typesDef; }
public void setTypesDef(AtlasTypesDef typesDef) { this.typesDef = typesDef; }
public Map<String, AtlasEntity> getEntities() { return entities; }
public void setEntities(Map<String, AtlasEntity> entities) { this.entities = entities; }
public Map<String, List<String>> getEntityCreationOrder() { return entityCreationOrder; }
public void setEntityCreationOrder(Map<String, List<String>> entityCreationOrder) { this.entityCreationOrder = entityCreationOrder; }
public void clear() {
if (entityCreationOrder != null) {
entityCreationOrder.clear();
}
if (typesDef != null) {
typesDef.clear();
}
if (entities != null) {
entities.clear();
}
}
public StringBuilder toString(StringBuilder sb) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append("AtlasExportData{");
sb.append("typesDef={").append(typesDef).append("}");
sb.append("entities={");
AtlasBaseTypeDef.dumpObjects(entities, sb);
sb.append("}");
sb.append("entityCreationOrder={");
AtlasBaseTypeDef.dumpObjects(entityCreationOrder, sb);
sb.append("}");
sb.append("}");
return sb;
}
@Override
public String toString() {
return toString(new StringBuilder()).toString();
}
}
}
\ No newline at end of file
......@@ -24,6 +24,7 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
......@@ -45,21 +46,23 @@ public class AtlasTypesDef {
private List<AtlasEntityDef> entityDefs;
public AtlasTypesDef() {
enumDefs = new ArrayList<>();
structDefs = new ArrayList<>();
enumDefs = new ArrayList<>();
structDefs = new ArrayList<>();
classificationDefs = new ArrayList<>();
entityDefs = new ArrayList<>();
entityDefs = new ArrayList<>();
}
public AtlasTypesDef(List<AtlasEnumDef> enumDefs, List<AtlasStructDef> structDefs,
public AtlasTypesDef(List<AtlasEnumDef> enumDefs,
List<AtlasStructDef> structDefs,
List<AtlasClassificationDef> classificationDefs,
List<AtlasEntityDef> entityDefs) {
List<AtlasEntityDef> entityDefs) {
this.enumDefs = enumDefs;
this.structDefs = structDefs;
this.classificationDefs = classificationDefs;
this.entityDefs = entityDefs;
}
public List<AtlasEnumDef> getEnumDefs() {
return enumDefs;
}
......@@ -80,10 +83,6 @@ public class AtlasTypesDef {
return classificationDefs;
}
public void setClassificationDefs(List<AtlasClassificationDef> classificationDefs) {
this.classificationDefs = classificationDefs;
}
public List<AtlasEntityDef> getEntityDefs() {
return entityDefs;
}
......@@ -92,6 +91,40 @@ public class AtlasTypesDef {
this.entityDefs = entityDefs;
}
public void setClassificationDefs(List<AtlasClassificationDef> classificationDefs) {
this.classificationDefs = classificationDefs;
}
public boolean hasClassificationDef(String name) {
return hasTypeDef(classificationDefs, name);
}
public boolean hasEnumDef(String name) {
return hasTypeDef(enumDefs, name);
}
public boolean hasStructDef(String name) {
return hasTypeDef(structDefs, name);
}
public boolean hasEntityDef(String name) {
return hasTypeDef(entityDefs, name);
}
private <T extends AtlasBaseTypeDef> boolean hasTypeDef(Collection<T> typeDefs, String name) {
if (CollectionUtils.isNotEmpty(typeDefs)) {
for (T typeDef : typeDefs) {
if (typeDef.getName().equals(name)) {
return true;
}
}
}
return false;
}
@JsonIgnore
public boolean isEmpty() {
return CollectionUtils.isEmpty(enumDefs) &&
......@@ -99,4 +132,49 @@ public class AtlasTypesDef {
CollectionUtils.isEmpty(classificationDefs) &&
CollectionUtils.isEmpty(entityDefs);
}
public void clear() {
if (enumDefs != null) {
enumDefs.clear();
}
if (structDefs != null) {
structDefs.clear();
}
if (classificationDefs != null) {
classificationDefs.clear();
}
if (entityDefs != null) {
entityDefs.clear();
}
}
public StringBuilder toString(StringBuilder sb) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append("AtlasTypesDef{");
sb.append("enumDefs={");
AtlasBaseTypeDef.dumpObjects(enumDefs, sb);
sb.append("}");
sb.append("structDefs={");
AtlasBaseTypeDef.dumpObjects(structDefs, sb);
sb.append("}");
sb.append("classificationDefs={");
AtlasBaseTypeDef.dumpObjects(classificationDefs, sb);
sb.append("}");
sb.append("entityDefs={");
AtlasBaseTypeDef.dumpObjects(entityDefs, sb);
sb.append("}");
return sb;
}
@Override
public String toString() {
return toString(new StringBuilder()).toString();
}
}
......@@ -91,29 +91,11 @@ public class AtlasEntityStoreV1 implements AtlasEntityStore {
LOG.debug("Retrieving entity with type={} and attributes={}: values={}", entityTypeName, uniqAttributes);
}
AtlasGraphQuery query = graph.query();
for (Map.Entry<String, Object> e : uniqAttributes.entrySet()) {
String attrName = e.getKey();
Object attrValue = e.getValue();
query = query.has(entityType.getQualifiedAttributeName(attrName), attrValue);
}
Iterator<AtlasVertex> result = query.has(Constants.ENTITY_TYPE_PROPERTY_KEY, entityTypeName)
.has(Constants.STATE_PROPERTY_KEY, Status.ACTIVE.name())
.vertices().iterator();
AtlasVertex entityVertex = result.hasNext() ? result.next() : null;
if (entityVertex == null) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, entityTypeName, uniqAttributes.keySet().toString(), uniqAttributes.values().toString());
}
String guid = GraphHelper.getGuid(entityVertex);
AtlasVertex entityVertex = AtlasGraphUtilsV1.getVertexByUniqueAttributes(entityType, uniqAttributes);
EntityGraphRetriever entityRetriever = new EntityGraphRetriever(typeRegistry);
return entityRetriever.toAtlasEntityWithExtInfo(guid);
return entityRetriever.toAtlasEntityWithExtInfo(entityVertex);
}
@Override
......
......@@ -26,19 +26,20 @@ import org.apache.atlas.model.TypeCategory;
import org.apache.atlas.model.instance.AtlasEntity;
import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
import org.apache.atlas.repository.Constants;
import org.apache.atlas.repository.graph.AtlasGraphProvider;
import org.apache.atlas.repository.graph.GraphHelper;
import org.apache.atlas.repository.graphdb.AtlasEdge;
import org.apache.atlas.repository.graphdb.AtlasElement;
import org.apache.atlas.repository.graphdb.AtlasGraphQuery;
import org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.type.AtlasEntityType;
import org.apache.atlas.type.AtlasStructType;
import org.apache.atlas.type.AtlasType;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.*;
/**
* Utility methods for Graph.
......@@ -199,6 +200,29 @@ public class AtlasGraphUtilsV1 {
return returnType.cast(property);
}
public static AtlasVertex getVertexByUniqueAttributes(AtlasEntityType entityType, Map<String, Object> uniqAttributes) throws AtlasBaseException {
AtlasGraphQuery query = AtlasGraphProvider.getGraphInstance().query();
for (Map.Entry<String, Object> e : uniqAttributes.entrySet()) {
String attrName = e.getKey();
Object attrValue = e.getValue();
query = query.has(entityType.getQualifiedAttributeName(attrName), attrValue);
}
Iterator<AtlasVertex> result = query.has(Constants.ENTITY_TYPE_PROPERTY_KEY, entityType.getTypeName())
.has(Constants.STATE_PROPERTY_KEY, AtlasEntity.Status.ACTIVE.name())
.vertices().iterator();
AtlasVertex entityVertex = result.hasNext() ? result.next() : null;
if (entityVertex == null) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, entityType.getTypeName(),
uniqAttributes.keySet().toString(), uniqAttributes.values().toString());
}
return entityVertex;
}
private static String toString(AtlasElement element) {
if (element instanceof AtlasVertex) {
return toString((AtlasVertex) element);
......
......@@ -31,12 +31,8 @@ import org.apache.atlas.repository.graph.GraphHelper;
import org.apache.atlas.repository.graphdb.AtlasEdge;
import org.apache.atlas.repository.graphdb.AtlasEdgeDirection;
import org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.type.AtlasArrayType;
import org.apache.atlas.type.AtlasMapType;
import org.apache.atlas.type.AtlasStructType;
import org.apache.atlas.type.*;
import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
import org.apache.atlas.type.AtlasType;
import org.apache.atlas.type.AtlasTypeRegistry;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -78,6 +74,10 @@ public final class EntityGraphRetriever {
return toAtlasEntity(getEntityVertex(guid));
}
public AtlasEntity toAtlasEntity(AtlasObjectId objId) throws AtlasBaseException {
return toAtlasEntity(getEntityVertex(objId));
}
public AtlasEntity toAtlasEntity(AtlasVertex entityVertex) throws AtlasBaseException {
return mapVertexToAtlasEntity(entityVertex, null);
}
......@@ -86,6 +86,10 @@ public final class EntityGraphRetriever {
return toAtlasEntityWithExtInfo(getEntityVertex(guid));
}
public AtlasEntityWithExtInfo toAtlasEntityWithExtInfo(AtlasObjectId objId) throws AtlasBaseException {
return toAtlasEntityWithExtInfo(getEntityVertex(objId));
}
public AtlasEntityWithExtInfo toAtlasEntityWithExtInfo(AtlasVertex entityVertex) throws AtlasBaseException {
AtlasEntityExtInfo entityExtInfo = new AtlasEntityExtInfo();
AtlasEntity entity = mapVertexToAtlasEntity(entityVertex, entityExtInfo);
......@@ -104,6 +108,24 @@ public final class EntityGraphRetriever {
}
}
private AtlasVertex getEntityVertex(AtlasObjectId objId) throws AtlasBaseException {
try {
if (! objId.isValid()) {
throw new AtlasBaseException(AtlasErrorCode.INVALID_OBJECT_ID, objId.toString());
}
if (objId.isAssignedGuid()) {
return graphHelper.getVertexForGUID(objId.getGuid());
} else {
AtlasEntityType entityType = typeRegistry.getEntityTypeByName(objId.getTypeName());
Map<String, Object> uniqAttributes = objId.getUniqueAttributes();
return AtlasGraphUtilsV1.getVertexByUniqueAttributes(entityType, uniqAttributes);
}
} catch (AtlasException excp) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, objId.toString());
}
}
private AtlasEntity mapVertexToAtlasEntity(AtlasVertex entityVertex, AtlasEntityExtInfo entityExtInfo) throws AtlasBaseException {
String guid = GraphHelper.getGuid(entityVertex);
AtlasEntity entity = entityExtInfo != null ? entityExtInfo.getEntity(guid) : null;
......
......@@ -20,11 +20,17 @@ package org.apache.atlas.web.resources;
import com.google.inject.Inject;
import org.apache.atlas.AtlasClient;
import org.apache.atlas.AtlasException;
import org.apache.atlas.discovery.DiscoveryService;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.impexp.AtlasExportRequest;
import org.apache.atlas.model.impexp.AtlasExportResult;
import org.apache.atlas.model.metrics.AtlasMetrics;
import org.apache.atlas.services.MetricsService;
import org.apache.atlas.authorize.AtlasActionTypes;
import org.apache.atlas.authorize.AtlasResourceTypes;
import org.apache.atlas.authorize.simple.AtlasAuthorizationUtils;
import org.apache.atlas.type.AtlasTypeRegistry;
import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter;
import org.apache.atlas.web.service.ServiceState;
import org.apache.atlas.web.util.Servlets;
......@@ -40,16 +46,17 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.io.IOException;
import java.util.*;
import static org.apache.atlas.web.adapters.AtlasInstanceRestAdapters.toAtlasBaseException;
/**
* Jersey Resource for admin operations.
......@@ -59,6 +66,12 @@ import java.util.Set;
public class AdminResource {
private static final Logger LOG = LoggerFactory.getLogger(AdminResource.class);
@Context
private HttpServletRequest httpServletRequest;
@Context
private HttpServletResponse httpServletResponse;
private static final String isCSRF_ENABLED = "atlas.rest-csrf.enabled";
private static final String BROWSER_USER_AGENT_PARAM = "atlas.rest-csrf.browser-useragents-regex";
private static final String CUSTOM_METHODS_TO_IGNORE_PARAM = "atlas.rest-csrf.methods-to-ignore";
......@@ -69,13 +82,19 @@ public class AdminResource {
private static final String editableEntityTypes = "atlas.ui.editable.entity.types";
private static final String DEFAULT_EDITABLE_ENTITY_TYPES = "hdfs_path,hdfs_path,hbase_table,hbase_column,hbase_column_family,kafka_topic";
private Response version;
private ServiceState serviceState;
private MetricsService metricsService;
private final ServiceState serviceState;
private final MetricsService metricsService;
private final DiscoveryService discoveryService;
private final AtlasTypeRegistry typeRegistry;
@Inject
public AdminResource(ServiceState serviceState, MetricsService metricsService) {
this.serviceState = serviceState;
this.metricsService = metricsService;
public AdminResource(ServiceState serviceState, MetricsService metricsService,
DiscoveryService discoveryService, AtlasTypeRegistry typeRegistry) {
this.serviceState = serviceState;
this.metricsService = metricsService;
this.discoveryService = discoveryService;
this.typeRegistry = typeRegistry;
}
/**
......@@ -249,6 +268,40 @@ public class AdminResource {
return metrics;
}
@POST
@Path("/export")
@Consumes(Servlets.JSON_MEDIA_TYPE)
public Response export(AtlasExportRequest request) throws AtlasBaseException {
ZipSink exportSink = null;
try {
exportSink = new ZipSink();
ExportService exportService = new ExportService(this.typeRegistry);
AtlasExportResult result = exportService.run(exportSink, request, Servlets.getUserName(httpServletRequest),
Servlets.getHostName(httpServletRequest),
Servlets.getRequestIpAddress(httpServletRequest));
exportSink.close();
ServletOutputStream outStream = httpServletResponse.getOutputStream();
exportSink.writeTo(outStream);
httpServletResponse.setContentType("application/zip");
httpServletResponse.setHeader("Content-Disposition",
"attachment; filename=" + result.getClass().getSimpleName());
outStream.flush();
return Response.ok().build();
} catch (AtlasException | IOException ex) {
LOG.error("export() failed", ex);
throw toAtlasBaseException(new AtlasException(ex));
} finally {
if (exportSink != null)
exportSink.close();
}
}
private String getEditableEntityTypes(PropertiesConfiguration config) {
String ret = DEFAULT_EDITABLE_ENTITY_TYPES;
......@@ -256,11 +309,11 @@ public class AdminResource {
Object value = config.getProperty(editableEntityTypes);
if (value instanceof String) {
ret = (String)value;
ret = (String) value;
} else if (value instanceof Collection) {
StringBuilder sb = new StringBuilder();
for (Object elem : ((Collection)value)) {
for (Object elem : ((Collection) value)) {
if (sb.length() > 0) {
sb.append(",");
}
......@@ -274,4 +327,4 @@ public class AdminResource {
return ret;
}
}
}
\ No newline at end of file
/**
* 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 org.apache.atlas.model.instance.AtlasClassification;
import org.apache.atlas.model.instance.AtlasObjectId;
import org.apache.atlas.model.typedef.AtlasTypesDef;
import org.apache.atlas.repository.graph.AtlasGraphProvider;
import org.apache.atlas.repository.graphdb.AtlasGraph;
import org.apache.atlas.repository.store.graph.v1.EntityGraphRetriever;
import org.apache.atlas.type.AtlasTypeRegistry;
import org.apache.atlas.AtlasException;
import org.apache.atlas.AtlasServiceException;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.instance.AtlasEntity;
import org.apache.atlas.model.impexp.*;
import org.apache.atlas.model.typedef.AtlasClassificationDef;
import org.apache.atlas.model.typedef.AtlasEntityDef;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.script.ScriptException;
import java.util.*;
public class ExportService {
private static final Logger LOG = LoggerFactory.getLogger(ExportService.class);
private final AtlasTypeRegistry typeRegistry;
private final AtlasGraph atlasGraph;
private final EntityGraphRetriever entityGraphRetriever;
public ExportService(final AtlasTypeRegistry typeRegistry) {
this.typeRegistry = typeRegistry;
this.entityGraphRetriever = new EntityGraphRetriever(this.typeRegistry);
this.atlasGraph = AtlasGraphProvider.getGraphInstance();
}
private class ExportContext {
final Map<String, Boolean> entitiesToBeProcessed = new HashMap<>();
final AtlasExportResult result;
final ZipSink sink;
long numOfEntitiesExported = 0;
ExportContext(AtlasExportResult result, ZipSink sink) {
this.result = result;
this.sink = sink;
}
}
public AtlasExportResult run(ZipSink exportSink, AtlasExportRequest request, String userName, String hostName,
String requestingIP) throws AtlasException {
ExportContext context = new ExportContext(new AtlasExportResult(request, userName, hostName, requestingIP, System.currentTimeMillis()), exportSink);
try {
LOG.info("==> export(user={}, from={})", userName, requestingIP);
int i = 0;
for (AtlasObjectId item : request.getItemsToExport()) {
process(Integer.toString(i++), item, context);
}
context.sink.setExportOrder(context.result.getData().getEntityCreationOrder());
context.sink.setTypesDef(context.result.getData().getTypesDef());
context.result.getData().clear();
context.result.setOperationStatus(AtlasExportResult.OperationStatus.SUCCESS);
context.sink.setResult(context.result);
}
catch(Exception ex) {
LOG.error("Operation failed: ", ex);
} finally {
LOG.info("<== export(user={}, from={}): status {}", userName, requestingIP, context.result.getOperationStatus());
}
return context.result;
}
private void process(String folder, AtlasObjectId item, ExportContext context) throws AtlasServiceException, AtlasException, AtlasBaseException {
try {
AtlasEntity entity = entityGraphRetriever.toAtlasEntity(item);
List<String> queue = populateConnectedEntities(entity.getGuid(), context);
process(entity, context);
for (String guid : queue) {
if(context.entitiesToBeProcessed.get(guid)) {
continue;
}
process(entityGraphRetriever.toAtlasEntity(guid), context);
}
context.result.getData().getEntityCreationOrder().put(folder, queue);
} catch (AtlasBaseException e) {
context.result.setOperationStatus(AtlasExportResult.OperationStatus.PARTIAL_SUCCESS);
LOG.error("Fetching entity failed for: {}", item);
}
}
private void process(AtlasEntity entity, ExportContext context) throws AtlasBaseException, AtlasException {
addTypesAsNeeded(entity.getTypeName(), context);
addClassificationsAsNeeded(entity, context);
addEntity(entity, context);
}
private void addEntity(AtlasEntity entity, ExportContext context) throws AtlasException, AtlasBaseException {
context.entitiesToBeProcessed.put(entity.getGuid(), true);
context.sink.add(entity);
context.result.incrementMeticsCounter(String.format("entity:%s", entity.getTypeName()));
context.result.incrementMeticsCounter("Entities");
context.numOfEntitiesExported++;
if (context.numOfEntitiesExported % 10 == 0) {
LOG.info("export(): in progress.. number of entities exported: {}", context.numOfEntitiesExported);
}
}
private List<String> populateConnectedEntities(String startGuid, ExportContext context) {
final String gremlinQuery = "g.V('__guid', '%s').bothE().bothV().has('__guid').__guid.toList()";
Map<String, Boolean> entitiesToBeProcessed = context.entitiesToBeProcessed;
List<String> queue = new ArrayList<>();
entitiesToBeProcessed.put(startGuid, false);
queue.add(startGuid);
for (int i=0; i < queue.size(); i++) {
String currentGuid = queue.get(i);
try {
List<String> result = (List<String>) atlasGraph.executeGremlinScript(
String.format(gremlinQuery, currentGuid), false);
for (String guid : result) {
if (entitiesToBeProcessed.containsKey(guid)) {
continue;
}
entitiesToBeProcessed.put(guid, false);
queue.add(guid);
}
} catch (ScriptException e) {
LOG.error("Child entities could not be added for %s", currentGuid);
}
}
return queue;
}
private void addClassificationsAsNeeded(AtlasEntity entity, ExportContext context) {
AtlasExportResult result = context.result;
AtlasTypesDef typesDef = result.getData().getTypesDef();
if(CollectionUtils.isNotEmpty(entity.getClassifications())) {
for (AtlasClassification c : entity.getClassifications()) {
if (typesDef.hasClassificationDef(c.getTypeName())) {
continue;
}
AtlasClassificationDef cd = typeRegistry.getClassificationDefByName(c.getTypeName());
typesDef.getClassificationDefs().add(cd);
result.incrementMeticsCounter("Classification");
}
}
}
private void addTypesAsNeeded(String typeName, ExportContext context) {
AtlasExportResult result = context.result;
AtlasTypesDef typesDef = result.getData().getTypesDef();
if(!typesDef.hasEntityDef(typeName)) {
AtlasEntityDef typeDefinition = typeRegistry.getEntityDefByName(typeName);
typesDef.getEntityDefs().add(typeDefinition);
result.incrementMeticsCounter("Type(s)");
}
}
}
/**
* 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 org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.instance.AtlasEntity;
import org.apache.atlas.model.impexp.AtlasExportResult;
import org.apache.atlas.model.typedef.AtlasTypesDef;
import org.apache.atlas.type.AtlasType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipSink {
private static final Logger LOG = LoggerFactory.getLogger(ZipSink.class);
private ZipOutputStream zipOutputStream;
private ByteArrayOutputStream byteArrayOutputStream;
public ZipSink() {
init();
}
private void init() {
byteArrayOutputStream = new ByteArrayOutputStream();
zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
}
public void add(AtlasEntity entity) throws AtlasBaseException {
String jsonData = convertToJSON(entity);
saveToZip(entity.getGuid(), jsonData);
}
public void setResult(AtlasExportResult result) throws AtlasBaseException {
final String fileName = "atlas-export-info";
String jsonData = convertToJSON(result);
saveToZip(fileName, jsonData);
}
public void setTypesDef(AtlasTypesDef typesDef) throws AtlasBaseException {
final String fileName = "atlas-typesdef";
String jsonData = convertToJSON(typesDef);
saveToZip(fileName, jsonData);
}
public void setExportOrder(Map<String, List<String>> result) throws AtlasBaseException {
final String fileName = "atlas-export-order";
String jsonData = convertToJSON(result);
saveToZip(fileName, jsonData);
}
public void writeTo(OutputStream stream) throws IOException {
byteArrayOutputStream.writeTo(stream);
}
public void close() {
try {
if(zipOutputStream != null) {
zipOutputStream.close();
zipOutputStream = null;
}
} catch (IOException e) {
LOG.error("Error closing Zip file", e);
}
}
private String convertToJSON(Object entity) {
return AtlasType.toJson(entity);
}
private void saveToZip(String fileName, String jsonData) throws AtlasBaseException {
try {
addToZipStream(fileName + ".json", jsonData);
} catch (IOException e) {
throw new AtlasBaseException(String.format("Error writing file %s.", fileName), e);
}
}
private void addToZipStream(String entryName, String payload) throws IOException {
ZipEntry e = new ZipEntry(entryName);
zipOutputStream.putNextEntry(e);
zipOutputStream.write(payload.getBytes());
zipOutputStream.closeEntry();
}
}
......@@ -24,6 +24,7 @@ import org.apache.atlas.utils.ParamChecker;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.codehaus.jettison.json.JSONException;
......@@ -37,6 +38,8 @@ import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.List;
......@@ -52,6 +55,7 @@ public final class Servlets {
}
public static final String JSON_MEDIA_TYPE = MediaType.APPLICATION_JSON + "; charset=UTF-8";
public static final String BINARY = MediaType.APPLICATION_OCTET_STREAM;
/**
* Returns the user of the given request.
......@@ -168,4 +172,25 @@ public final class Servlets {
ParamChecker.notNull(inputStr, "Input String cannot be null");
return StringEscapeUtils.escapeJson(inputStr);
}
public static String getRequestIpAddress(HttpServletRequest httpServletRequest) {
try {
InetAddress inetAddr = InetAddress.getByName(httpServletRequest.getRemoteAddr());
String ip = inetAddr.getHostAddress();
return ip;
} catch(UnknownHostException ex) {
LOG.error("Error occured when retrieving IP address", ex);
return "";
}
}
public static String getHostName(HttpServletRequest httpServletRequest) {
return httpServletRequest.getLocalName();
}
public static String getUserName(HttpServletRequest httpServletRequest) throws IOException {
return httpServletRequest.getRemoteUser();
}
}
/**
* 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;
public class AdminExportTest {
}
......@@ -48,7 +48,7 @@ public class AdminResourceTest {
when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.ACTIVE);
AdminResource adminResource = new AdminResource(serviceState, null);
AdminResource adminResource = new AdminResource(serviceState, null, null, null);
Response response = adminResource.getStatus();
assertEquals(response.getStatus(), HttpServletResponse.SC_OK);
JSONObject entity = (JSONObject) response.getEntity();
......@@ -59,7 +59,7 @@ public class AdminResourceTest {
public void testResourceGetsValueFromServiceState() throws JSONException {
when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE);
AdminResource adminResource = new AdminResource(serviceState, null);
AdminResource adminResource = new AdminResource(serviceState, null, null, null);
Response response = adminResource.getStatus();
verify(serviceState).getState();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment