| /** |
| * 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 |
| * <p> |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * <p> |
| * 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.repository.graphdb.janus.migration; |
| |
| import org.apache.atlas.model.typedef.AtlasRelationshipDef; |
| import org.apache.atlas.model.typedef.AtlasRelationshipDef.PropagateTags; |
| import org.apache.atlas.repository.Constants; |
| import org.apache.atlas.type.AtlasTypeRegistry; |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.tinkerpop.gremlin.structure.Vertex; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| |
| import static org.apache.atlas.repository.Constants.ATTRIBUTE_INDEX_PROPERTY_KEY; |
| import static org.apache.atlas.repository.Constants.ATTRIBUTE_KEY_PROPERTY_KEY; |
| import static org.apache.atlas.repository.Constants.CLASSIFICATION_EDGE_NAME_PROPERTY_KEY; |
| import static org.apache.atlas.repository.Constants.CLASSIFICATION_ENTITY_GUID; |
| import static org.apache.atlas.repository.Constants.CLASSIFICATION_VERTEX_PROPAGATE_KEY; |
| import static org.apache.atlas.repository.Constants.ENTITY_TYPE_PROPERTY_KEY; |
| import static org.apache.atlas.repository.Constants.RELATIONSHIPTYPE_TAG_PROPAGATION_KEY; |
| import static org.apache.atlas.repository.Constants.STATE_PROPERTY_KEY; |
| import static org.apache.atlas.repository.graphdb.janus.migration.TypesDefScrubber.*; |
| |
| public class ElementProcessors { |
| private static final Logger LOG = LoggerFactory.getLogger(ElementProcessors.class); |
| |
| public static final String PRIMITIVE_MAP_CATEGORY = "MAP_PRIMITIVE"; |
| public static final String NON_PRIMITIVE_MAP_CATEGORY = "MAP"; |
| public static final String NON_PRIMITIVE_ARRAY_CATEGORY = "ARRAY"; |
| private static final String[] NON_PRIMITIVE_KEYS = { ElementProcessors.NON_PRIMITIVE_ARRAY_CATEGORY }; |
| |
| private final Map<String, RelationshipCacheGenerator.TypeInfo> relationshipLookup; |
| private final Map<String, Map<String, List<String>>> postProcessMap; |
| private final Map<String, ClassificationToStructDefName> traitToTypeMap; |
| |
| private final NonPrimitiveListPropertyProcessor nonPrimitiveListPropertyProcessor = new NonPrimitiveListPropertyProcessor(); |
| private final NonPrimitiveMapPropertyProcessor nonPrimitiveMapPropertyProcessor = new NonPrimitiveMapPropertyProcessor(); |
| private final PrimitiveMapPropertyProcessor primitiveMapPropertyProcessor = new PrimitiveMapPropertyProcessor(); |
| private final EdgeCollectionPropertyProcessor edgeCollectionPropertyProcessor = new EdgeCollectionPropertyProcessor(); |
| private final EdgeRelationshipPropertyProcessor edgeRelationshipPropertyProcessor = new EdgeRelationshipPropertyProcessor(); |
| private final EdgeTraitTypesPropertyProcessor edgeTraitTypesPropertyProcessor = new EdgeTraitTypesPropertyProcessor(); |
| |
| public ElementProcessors(AtlasTypeRegistry typeRegistry, TypesDefScrubber scrubber) { |
| this(RelationshipCacheGenerator.get(typeRegistry), |
| TypesWithCollectionsFinder.getVertexPropertiesForCollectionAttributes(typeRegistry), |
| scrubber.getTraitToTypeMap()); |
| } |
| |
| ElementProcessors(Map<String, RelationshipCacheGenerator.TypeInfo> lookup, |
| Map<String, Map<String, List<String>>> postProcessMap, |
| Map<String, ClassificationToStructDefName> traitToTypeMap) { |
| this.relationshipLookup = lookup; |
| this.postProcessMap = postProcessMap; |
| this.traitToTypeMap = traitToTypeMap; |
| } |
| |
| public static String[] getNonPrimitiveCategoryKeys() { |
| return NON_PRIMITIVE_KEYS; |
| } |
| |
| public Map<String,Map<String, List<String>>> getPropertiesToPostProcess() { |
| return postProcessMap; |
| } |
| |
| public String addIndexKeysForCollections(Vertex out, Object edgeId, String label, Map<String, Object> edgeProperties) { |
| return edgeCollectionPropertyProcessor.update(out, edgeId, label, edgeProperties); |
| } |
| |
| public void processCollections(String typeNameKey, Map<String,Object> vertexProperties) { |
| if (!vertexProperties.containsKey(typeNameKey)) { |
| return; |
| } |
| |
| String typeName = (String) vertexProperties.get(typeNameKey); |
| |
| if (!postProcessMap.containsKey(typeName)) { |
| return; |
| } |
| |
| primitiveMapPropertyProcessor.update(typeName, vertexProperties); |
| nonPrimitiveMapPropertyProcessor.update(typeName, vertexProperties); |
| nonPrimitiveListPropertyProcessor.update(typeName, vertexProperties); |
| } |
| |
| public String updateEdge(Vertex in, Vertex out, Object edgeId, String label, Map<String,Object> props) { |
| return edgeRelationshipPropertyProcessor.update(in, out, edgeId, label, props); |
| } |
| |
| private class NonPrimitiveMapPropertyProcessor { |
| final String category = NON_PRIMITIVE_MAP_CATEGORY; |
| |
| public void update(String typeName, Map<String,Object> vertexProperties) { |
| if (!postProcessMap.containsKey(typeName)) { |
| return; |
| } |
| |
| if (!postProcessMap.get(typeName).containsKey(category)) { |
| return; |
| } |
| |
| List<String> propertyTypeList = postProcessMap.get(typeName).get(category); |
| |
| for (String property : propertyTypeList) { |
| if (!vertexProperties.containsKey(property)) { |
| continue; |
| } |
| |
| List<Object> list = (List<Object>) vertexProperties.get(property); |
| |
| if (list == null) { |
| continue; |
| } |
| |
| for (Object listEntry : list) { |
| String key = (String) listEntry; |
| String valueKey = getMapKey(property, key); |
| |
| if (vertexProperties.containsKey(valueKey)) { |
| vertexProperties.remove(valueKey); |
| } |
| } |
| |
| vertexProperties.remove(property); |
| } |
| } |
| |
| private String getMapKey(String property, String key) { |
| return String.format("%s.%s", property, key); |
| } |
| } |
| |
| private class PrimitiveMapPropertyProcessor { |
| final String category = PRIMITIVE_MAP_CATEGORY; |
| |
| public void update(String typeName, Map<String, Object> vertexProperties) { |
| if (!postProcessMap.get(typeName).containsKey(category)) { |
| return; |
| } |
| |
| List<String> propertyTypeList = postProcessMap.get(typeName).get(category); |
| |
| for (String property : propertyTypeList) { |
| if (!vertexProperties.containsKey(property)) { |
| continue; |
| } |
| |
| List<Object> list = (List<Object>) vertexProperties.get(property); |
| |
| if (list == null) { |
| continue; |
| } |
| |
| Map<String, Object> map = getAggregatedMap(vertexProperties, property, list); |
| |
| vertexProperties.put(property, map); |
| } |
| } |
| |
| private Map<String, Object> getAggregatedMap(Map<String, Object> vertexProperties, String property, List<Object> list) { |
| Map<String, Object> map = new HashMap<>(); |
| |
| for (Object listEntry : list) { |
| String key = (String) listEntry; |
| String valueKey = getMapKey(property, key); |
| |
| if (vertexProperties.containsKey(valueKey)) { |
| Object value = getValueFromProperties(valueKey, vertexProperties); |
| |
| vertexProperties.remove(valueKey); |
| |
| map.put(key, value); |
| } |
| } |
| |
| return map; |
| } |
| |
| private String getMapKey(String property, String key) { |
| return String.format("%s.%s", property, key); |
| } |
| |
| private Object getValueFromProperties(String key, Map<String, Object> vertexProperties) { |
| if (!vertexProperties.containsKey(key)) { |
| return null; |
| } |
| |
| return vertexProperties.get(key); |
| } |
| } |
| |
| private class NonPrimitiveListPropertyProcessor { |
| private final String category = NON_PRIMITIVE_ARRAY_CATEGORY; |
| |
| private void update(String typeName, Map<String,Object> props) { |
| if(!postProcessMap.get(typeName).containsKey(category)) { |
| return; |
| } |
| |
| List<String> propertyTypeList = postProcessMap.get(typeName).get(category); |
| for (String property : propertyTypeList) { |
| if(!props.containsKey(property)) { |
| continue; |
| } |
| |
| Map<String, String> listMap = getUpdatedEdgeList(props.get(property)); |
| |
| if(listMap == null) { |
| continue; |
| } |
| |
| props.put(property, listMap); |
| } |
| } |
| |
| private Map<String, String> getUpdatedEdgeList(Object o) { |
| Map<String, String> listMap = new HashMap<>(); |
| |
| if(!(o instanceof List)) { |
| return null; |
| } |
| |
| List list = (List) o; |
| |
| for (int i = 0; i < list.size(); i++) { |
| listMap.put((String) list.get(i), Integer.toString(i)); |
| } |
| |
| return listMap; |
| } |
| } |
| |
| private class EdgeTraitTypesPropertyProcessor { |
| private void update(String label, Vertex in) { |
| if (traitToTypeMap.size() == 0) { |
| return; |
| } |
| |
| if (!in.property(ENTITY_TYPE_PROPERTY_KEY).isPresent()) { |
| return; |
| } |
| |
| String typeName = (String) in.property(ENTITY_TYPE_PROPERTY_KEY).value(); |
| String key = label; |
| |
| if (!traitToTypeMap.containsKey(key)) { |
| key = StringUtils.substringBeforeLast(key, "."); |
| |
| if(!traitToTypeMap.containsKey(key)) { |
| return; |
| } |
| } |
| |
| if (!traitToTypeMap.get(key).getTypeName().equals(typeName)) { |
| return; |
| } |
| |
| in.property(ENTITY_TYPE_PROPERTY_KEY, traitToTypeMap.get(key).getLegacyTypeName()); |
| } |
| } |
| |
| private class EdgeRelationshipPropertyProcessor { |
| public String update(Vertex in, Vertex out, Object edgeId, String label, Map<String, Object> props) { |
| edgeTraitTypesPropertyProcessor.update(label, in); |
| |
| if(addRelationshipTypeForClassification(in, out, label, props)) { |
| label = Constants.CLASSIFICATION_LABEL; |
| } else { |
| addRelationshipTypeName(label, props); |
| |
| label = addIndexKeysForCollections(out, edgeId, label, props); |
| } |
| |
| addMandatoryRelationshipProperties(label, props); |
| |
| return label; |
| } |
| |
| private String getRelationshipTypeName(String label) { |
| return relationshipLookup.containsKey(label) ? relationshipLookup.get(label).getTypeName() : ""; |
| } |
| |
| private PropagateTags getDefaultPropagateValue(String label) { |
| return relationshipLookup.containsKey(label) ? |
| relationshipLookup.get(label).getPropagateTags() : |
| AtlasRelationshipDef.PropagateTags.NONE; |
| } |
| |
| private boolean addRelationshipTypeForClassification(Vertex in, Vertex out, String label, Map<String, Object> props) { |
| if (in.property(ENTITY_TYPE_PROPERTY_KEY).isPresent()) { |
| String inTypeName = (String) in.property(ENTITY_TYPE_PROPERTY_KEY).value(); |
| |
| if (StringUtils.isNotEmpty(inTypeName)) { |
| if (inTypeName.equals(label)) { |
| props.put(ENTITY_TYPE_PROPERTY_KEY, inTypeName); |
| props.put(CLASSIFICATION_EDGE_NAME_PROPERTY_KEY, inTypeName); |
| |
| addEntityGuidToTrait(in, out); |
| |
| return true; |
| } |
| } else { |
| LOG.info("Could not find typeName for trait: {}", label); |
| } |
| } |
| |
| return false; |
| } |
| |
| private void addEntityGuidToTrait(Vertex in, Vertex out) { |
| String entityGuid = ""; |
| |
| if (out.property(Constants.GUID_PROPERTY_KEY).isPresent()) { |
| entityGuid = (String) out.property(Constants.GUID_PROPERTY_KEY).value(); |
| } |
| |
| if(StringUtils.isNotEmpty(entityGuid)) { |
| in.property(CLASSIFICATION_ENTITY_GUID, entityGuid); |
| in.property(CLASSIFICATION_VERTEX_PROPAGATE_KEY, false); |
| } |
| } |
| |
| private void addRelationshipTypeName(String edgeLabel, Map<String, Object> props) { |
| String typeName = getRelationshipTypeName(edgeLabel); |
| |
| if (StringUtils.isNotEmpty(typeName)) { |
| props.put(ENTITY_TYPE_PROPERTY_KEY, typeName); |
| } else { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Could not find relationship type for: {}", edgeLabel); |
| } |
| } |
| } |
| |
| private void addMandatoryRelationshipProperties(String label, Map<String, Object> props) { |
| props.put(Constants.RELATIONSHIP_GUID_PROPERTY_KEY, UUID.randomUUID().toString()); |
| |
| props.put(RELATIONSHIPTYPE_TAG_PROPAGATION_KEY, String.valueOf(getDefaultPropagateValue(label))); |
| props.put(STATE_PROPERTY_KEY, "ACTIVE"); |
| } |
| } |
| |
| private class EdgeCollectionPropertyProcessor { |
| private static final int LABEL_INDEX = 0; |
| private static final int KEY_INDEX = 1; |
| |
| public String update(Vertex out, Object edgeId, String label, Map<String, Object> edgeProperties) { |
| String[] labelKeyPair = getNonPrimitiveArrayFromLabel(out, (String) edgeId, label); |
| |
| if (labelKeyPair != null) { |
| edgeProperties.put(ATTRIBUTE_INDEX_PROPERTY_KEY, Integer.valueOf(labelKeyPair[KEY_INDEX])); |
| |
| return label; |
| } |
| |
| labelKeyPair = getNonPrimitiveMapKeyFromLabel(out, label); |
| |
| if (labelKeyPair != null) { |
| label = labelKeyPair[LABEL_INDEX]; |
| |
| edgeProperties.put(ATTRIBUTE_KEY_PROPERTY_KEY, labelKeyPair[KEY_INDEX]); |
| } |
| |
| return label; |
| } |
| |
| private String[] getNonPrimitiveArrayFromLabel(Vertex v, String edgeId, String label) { |
| if (!v.property(ENTITY_TYPE_PROPERTY_KEY).isPresent()) { |
| return null; |
| } |
| |
| String typeName = (String) v.property(ENTITY_TYPE_PROPERTY_KEY).value(); |
| String propertyName = StringUtils.remove(label, Constants.INTERNAL_PROPERTY_KEY_PREFIX); |
| |
| if(!containsNonPrimitiveCollectionProperty(typeName, propertyName, NON_PRIMITIVE_ARRAY_CATEGORY)) { |
| return null; |
| } |
| |
| Map<String, String> edgeIdIndexList = (Map<String, String>) v.property(propertyName).value(); |
| |
| if (edgeIdIndexList.containsKey(edgeId)) { |
| return getLabelKeyPair(label, edgeIdIndexList.get(edgeId)); |
| } |
| |
| return null; |
| } |
| |
| // legacy edge label is in format: __<type name>.<key> |
| // label: in new format which is type name |
| // this method extracts: |
| // key: what remains of the legacy label string when '__' and type name are removed |
| private String[] getNonPrimitiveMapKeyFromLabel(Vertex v, String label) { |
| if (!v.property(ENTITY_TYPE_PROPERTY_KEY).isPresent()) { |
| return null; |
| } |
| |
| String typeName = (String) v.property(ENTITY_TYPE_PROPERTY_KEY).value(); |
| |
| if(!postProcessMap.containsKey(typeName)) { |
| return null; |
| } |
| |
| if(!postProcessMap.get(typeName).containsKey(NON_PRIMITIVE_MAP_CATEGORY)) { |
| return null; |
| } |
| |
| String propertyName = StringUtils.remove(label, Constants.INTERNAL_PROPERTY_KEY_PREFIX); |
| List<String> properties = postProcessMap.get(typeName).get(NON_PRIMITIVE_MAP_CATEGORY); |
| |
| for (String p : properties) { |
| if (propertyName.startsWith(p)) { |
| return getLabelKeyPair( |
| String.format("%s%s", Constants.INTERNAL_PROPERTY_KEY_PREFIX, p), |
| StringUtils.remove(propertyName, p).substring(1).trim()); |
| } |
| } |
| |
| return null; |
| } |
| |
| private boolean containsNonPrimitiveCollectionProperty(String typeName, String propertyName, String categoryType) { |
| if (!postProcessMap.containsKey(typeName)) { |
| return false; |
| } |
| |
| if (!postProcessMap.get(typeName).containsKey(categoryType)) { |
| return false; |
| } |
| |
| List<String> properties = postProcessMap.get(typeName).get(categoryType); |
| |
| for (String p : properties) { |
| if (p.equals(propertyName)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private String[] getLabelKeyPair(String label, String value) { |
| return new String[] { label, value }; |
| } |
| } |
| } |