http://git-wip-us.apache.org/repos/asf/atlas/blob/75415862/repository/src/main/java/org/apache/atlas/glossary/GlossaryService.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/glossary/GlossaryService.java 
b/repository/src/main/java/org/apache/atlas/glossary/GlossaryService.java
new file mode 100644
index 0000000..91abf5e
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/glossary/GlossaryService.java
@@ -0,0 +1,1159 @@
+/**
+ * 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.glossary;
+
+import org.apache.atlas.AtlasErrorCode;
+import org.apache.atlas.SortOrder;
+import org.apache.atlas.exception.AtlasBaseException;
+import org.apache.atlas.model.glossary.AtlasGlossary;
+import org.apache.atlas.model.glossary.AtlasGlossaryCategory;
+import org.apache.atlas.model.glossary.AtlasGlossaryTerm;
+import org.apache.atlas.model.glossary.relations.AtlasRelatedCategoryHeader;
+import org.apache.atlas.model.glossary.relations.AtlasRelatedTermHeader;
+import org.apache.atlas.model.glossary.relations.AtlasTermCategorizationHeader;
+import org.apache.atlas.model.instance.AtlasEntityHeader;
+import org.apache.atlas.model.instance.AtlasObjectId;
+import org.apache.atlas.model.instance.AtlasRelationship;
+import org.apache.atlas.repository.ogm.DataAccess;
+import org.apache.atlas.repository.store.graph.AtlasRelationshipStore;
+import org.apache.atlas.repository.store.graph.v1.AtlasGraphUtilsV1;
+import org.apache.commons.collections.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.inject.Inject;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+public class GlossaryService {
+    private static final Logger  LOG           = 
LoggerFactory.getLogger(GlossaryService.class);
+    private static final boolean DEBUG_ENABLED = LOG.isDebugEnabled();
+
+    private static final String ATLAS_GLOSSARY_PREFIX = "__AtlasGlossary";
+    // Relation name constants
+    private static final String TERM_ANCHOR           = ATLAS_GLOSSARY_PREFIX 
+ "TermAnchor";
+    private static final String CATEGORY_ANCHOR       = ATLAS_GLOSSARY_PREFIX 
+ "CategoryAnchor";
+    private static final String CATEGORY_HIERARCHY    = ATLAS_GLOSSARY_PREFIX 
+ "CategoryHierarchyLink";
+    private static final String TERM_CATEGORIZATION   = ATLAS_GLOSSARY_PREFIX 
+ "TermCategorization";
+    private static final String TERM_ASSIGNMENT       = ATLAS_GLOSSARY_PREFIX 
+ "SemanticAssignment";
+
+    private final DataAccess             dataAccess;
+    private final AtlasRelationshipStore relationshipStore;
+
+    @Inject
+    public GlossaryService(DataAccess dataAccess, final AtlasRelationshipStore 
relationshipStore) {
+        this.dataAccess = dataAccess;
+        this.relationshipStore = relationshipStore;
+    }
+
+
+    /**
+     * List all glossaries
+     *
+     * @param limit     page size - no paging by default
+     * @param offset    offset for pagination
+     * @param sortOrder ASC (default) or DESC
+     * @return List of all glossaries
+     * @throws AtlasBaseException
+     */
+    public List<AtlasGlossary> getGlossaries(int limit, int offset, SortOrder 
sortOrder) throws AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getGlossaries({}, {}, {})", limit, 
offset, sortOrder);
+        }
+
+        List<String>     glossaryGuids    = 
AtlasGraphUtilsV1.findEntityGUIDsByType(ATLAS_GLOSSARY_PREFIX, sortOrder);
+        PaginationHelper paginationHelper = new 
PaginationHelper<>(glossaryGuids, offset, limit);
+
+        List<AtlasGlossary> ret;
+        List<String>        guidsToLoad = paginationHelper.getPaginatedList();
+        if (CollectionUtils.isNotEmpty(guidsToLoad)) {
+            ret = 
guidsToLoad.stream().map(GlossaryService::getGlossarySkeleton).collect(Collectors.toList());
+            Iterable<AtlasGlossary> glossaries = dataAccess.load(ret);
+            ret.clear();
+
+            glossaries.forEach(ret::add);
+        } else {
+            ret = Collections.emptyList();
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getGlossaries() : {}", ret);
+        }
+        return ret;
+    }
+
+    /**
+     * Create a glossary
+     *
+     * @param atlasGlossary Glossary specification to be created
+     * @return Glossary definition
+     * @throws AtlasBaseException
+     */
+    public AtlasGlossary createGlossary(AtlasGlossary atlasGlossary) throws 
AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.createGlossary({})", atlasGlossary);
+        }
+
+        if (Objects.isNull(atlasGlossary)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Glossary 
definition missing");
+        }
+        if (Objects.isNull(atlasGlossary.getQualifiedName())) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Glossary 
qualifiedName is mandatory");
+
+        }
+        AtlasGlossary saved = dataAccess.save(atlasGlossary);
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.createGlossary() : {}", saved);
+        }
+        return saved;
+    }
+
+    /**
+     * Get specific glossary
+     *
+     * @param glossaryGuid unique identifier
+     * @return Glossary corresponding to specified glossaryGuid
+     * @throws AtlasBaseException
+     */
+    public AtlasGlossary getGlossary(String glossaryGuid) throws 
AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getGlossary({})", glossaryGuid);
+        }
+
+        if (Objects.isNull(glossaryGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"glossaryGuid is null/empty");
+        }
+
+        AtlasGlossary atlasGlossary = getGlossarySkeleton(glossaryGuid);
+        AtlasGlossary ret           = dataAccess.load(atlasGlossary);
+
+        setDisplayTextForRelations(ret);
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getGlossary() : {}", ret);
+        }
+        return ret;
+    }
+
+    /**
+     * Get specific glossary
+     *
+     * @param glossaryGuid unique identifier
+     * @return Glossary corresponding to specified glossaryGuid
+     * @throws AtlasBaseException
+     */
+    public AtlasGlossary.AtlasGlossaryExtInfo getDetailedGlossary(String 
glossaryGuid) throws AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getGlossary({})", glossaryGuid);
+        }
+
+        if (Objects.isNull(glossaryGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"glossaryGuid is null/empty");
+        }
+
+        AtlasGlossary atlasGlossary = getGlossarySkeleton(glossaryGuid);
+        AtlasGlossary glossary      = dataAccess.load(atlasGlossary);
+
+        AtlasGlossary.AtlasGlossaryExtInfo ret = new 
AtlasGlossary.AtlasGlossaryExtInfo(glossary);
+
+        // Load all linked terms
+        if (CollectionUtils.isNotEmpty(ret.getTerms())) {
+            List<AtlasGlossaryTerm> termsToLoad = ret.getTerms()
+                                                     .stream()
+                                                     .map(id -> 
getAtlasGlossaryTermSkeleton(id.getTermGuid()))
+                                                     
.collect(Collectors.toList());
+            Iterable<AtlasGlossaryTerm> glossaryTerms = 
dataAccess.load(termsToLoad);
+            glossaryTerms.forEach(ret::addTermInfo);
+        }
+
+        // Load all linked categories
+        if (CollectionUtils.isNotEmpty(ret.getCategories())) {
+            List<AtlasGlossaryCategory> categoriesToLoad = ret.getCategories()
+                                                              .stream()
+                                                              .map(id -> 
getAtlasGlossaryCategorySkeleton(id.getCategoryGuid()))
+                                                              
.collect(Collectors.toList());
+            Iterable<AtlasGlossaryCategory> glossaryCategories = 
dataAccess.load(categoriesToLoad);
+            glossaryCategories.forEach(ret::addCategoryInfo);
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getGlossary() : {}", ret);
+        }
+        return ret;
+    }
+
+    public AtlasGlossary updateGlossary(AtlasGlossary atlasGlossary) throws 
AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.updateGlossary({})", atlasGlossary);
+        }
+        if (Objects.isNull(atlasGlossary)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Glossary 
is null/empty");
+        }
+
+        AtlasGlossary ret = dataAccess.load(atlasGlossary);
+
+        if (!ret.equals(atlasGlossary)) {
+            atlasGlossary.setGuid(ret.getGuid());
+            atlasGlossary.setQualifiedName(ret.getQualifiedName());
+
+            ret = dataAccess.save(atlasGlossary);
+            setDisplayTextForRelations(ret);
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.updateGlossary() : {}", ret);
+        }
+        return ret;
+    }
+
+    public void deleteGlossary(String glossaryGuid) throws AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.deleteGlossary({})", glossaryGuid);
+        }
+        if (Objects.isNull(glossaryGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"glossaryGuid is null/empty");
+        }
+
+
+        // FIXME: When deleting all other related entities, the new edge label 
(r:<Relation>) is failing the delete calls
+        dataAccess.delete(glossaryGuid);
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.deleteGlossary()");
+        }
+    }
+
+    /*
+     * GlossaryTerms related operations
+     * */
+    public AtlasGlossaryTerm getTerm(String termGuid) throws 
AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getTerm({})", termGuid);
+        }
+        if (Objects.isNull(termGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "termGuid 
is null/empty");
+        }
+
+
+        AtlasGlossaryTerm atlasGlossary = 
getAtlasGlossaryTermSkeleton(termGuid);
+        AtlasGlossaryTerm ret           = dataAccess.load(atlasGlossary);
+
+        setDisplayTextForRelations(ret);
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getTerm() : {}", ret);
+        }
+        return ret;
+    }
+
+    public AtlasGlossaryTerm createTerm(AtlasGlossaryTerm glossaryTerm) throws 
AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.create({})", glossaryTerm);
+        }
+        if (Objects.isNull(glossaryTerm)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"GlossaryTerm definition missing");
+        }
+        if (Objects.isNull(glossaryTerm.getQualifiedName())) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"GlossaryTerm qualifiedName is mandatory");
+        }
+
+        AtlasGlossaryTerm saved = dataAccess.save(glossaryTerm);
+
+        // TODO: Create all term relations
+        processTermAnchor(glossaryTerm, saved);
+        processRelatedTerms(glossaryTerm, saved);
+        processAssociatedCategories(glossaryTerm, saved);
+
+        saved = dataAccess.load(glossaryTerm);
+        setDisplayTextForRelations(saved);
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.create() : {}", saved);
+        }
+        return saved;
+    }
+
+    public List<AtlasGlossaryTerm> createTerms(List<AtlasGlossaryTerm> 
glossaryTerm) throws AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.create({})", glossaryTerm);
+        }
+
+        if (Objects.isNull(glossaryTerm)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"glossaryTerm(s) is null/empty");
+        }
+
+
+        List<AtlasGlossaryTerm> ret = new ArrayList<>();
+        for (AtlasGlossaryTerm atlasGlossaryTerm : glossaryTerm) {
+            ret.add(createTerm(atlasGlossaryTerm));
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== GlossaryService.createTerms() : {}", ret);
+        }
+
+        return ret;
+    }
+
+    public AtlasGlossaryTerm updateTerm(AtlasGlossaryTerm atlasGlossaryTerm) 
throws AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.updateTerm({})", atlasGlossaryTerm);
+        }
+        if (Objects.isNull(atlasGlossaryTerm)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"atlasGlossaryTerm is null/empty");
+        }
+
+
+        AtlasGlossaryTerm existing = dataAccess.load(atlasGlossaryTerm);
+        AtlasGlossaryTerm updated  = atlasGlossaryTerm;
+        if (!existing.equals(updated)) {
+            try {
+                updated.setGuid(existing.getGuid());
+                updated.setQualifiedName(existing.getQualifiedName());
+
+                atlasGlossaryTerm = dataAccess.save(updated);
+            } catch (AtlasBaseException e) {
+                LOG.debug("Glossary term had no immediate attr updates. 
Exception: {}", e.getMessage());
+            } finally {
+                // TODO: Manage remaining term relations
+                processRelations(atlasGlossaryTerm, existing);
+            }
+
+        }
+
+        updated = dataAccess.load(atlasGlossaryTerm);
+        setDisplayTextForRelations(updated);
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.updateTerm() : {}", updated);
+        }
+        return updated;
+    }
+
+    public void deleteTerm(String termGuid) throws AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.deleteTerm({})", termGuid);
+        }
+        if (Objects.isNull(termGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "termGuid 
is null/empty");
+        }
+
+
+        dataAccess.delete(termGuid);
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.deleteTerm()");
+        }
+    }
+
+    public void assignTermToEntities(String termGuid, 
Collection<AtlasEntityHeader> entityHeaders) throws AtlasBaseException {
+        AtlasGlossaryTerm glossaryTerm = 
dataAccess.load(getAtlasGlossaryTermSkeleton(termGuid));
+
+        if (Objects.nonNull(glossaryTerm)) {
+            Set<AtlasEntityHeader> assignedEntities = 
glossaryTerm.getAssignedEntities();
+            for (AtlasEntityHeader entityHeader : entityHeaders) {
+                if (CollectionUtils.isNotEmpty(assignedEntities) && 
assignedEntities.contains(entityHeader)) continue;
+                if (DEBUG_ENABLED) {
+                    LOG.debug("Assigning term guid={}, to entity guid = {}", 
termGuid, entityHeader.getGuid());
+                }
+                createRelationship(defineTermAssignment(termGuid, 
entityHeader));
+            }
+        }
+    }
+
+    public void removeTermFromEntities(String termGuid, 
Collection<AtlasEntityHeader> entityHeaders) throws AtlasBaseException {
+        AtlasGlossaryTerm glossaryTerm = 
dataAccess.load(getAtlasGlossaryTermSkeleton(termGuid));
+
+        if (Objects.nonNull(glossaryTerm)) {
+            for (AtlasEntityHeader entityHeader : entityHeaders) {
+                if (DEBUG_ENABLED) {
+                    LOG.debug("Removing term guid={}, from entity guid = {}", 
termGuid, entityHeader.getGuid());
+                }
+                Object relationGuid = 
entityHeader.getAttribute("relationGuid");
+                if (Objects.isNull(relationGuid)) {
+                    throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"EntityHeader is missing mandatory attribute relation guid");
+                }
+                relationshipStore.deleteById((String) relationGuid);
+            }
+        }
+    }
+
+    /*
+     * GlossaryCategory related operations
+     * */
+    public AtlasGlossaryCategory getCategory(String categoryGuid) throws 
AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getCategory({})", categoryGuid);
+        }
+        if (Objects.isNull(categoryGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"categoryGuid is null/empty");
+        }
+
+
+        AtlasGlossaryCategory atlasGlossary = 
getAtlasGlossaryCategorySkeleton(categoryGuid);
+        AtlasGlossaryCategory ret           = dataAccess.load(atlasGlossary);
+
+        setDisplayTextForRelations(ret);
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getCategory() : {}", ret);
+        }
+        return ret;
+    }
+
+    public AtlasGlossaryCategory createCategory(AtlasGlossaryCategory 
glossaryCategory) throws AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.createCategory({})", 
glossaryCategory);
+        }
+
+        if (Objects.isNull(glossaryCategory)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"GlossaryCategory definition missing");
+        }
+        if (Objects.isNull(glossaryCategory.getQualifiedName())) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"GlossaryCategory qualifiedName is mandatory");
+        }
+
+        AtlasGlossaryCategory saved = dataAccess.save(glossaryCategory);
+
+        // Attempt relation creation
+        if (Objects.nonNull(glossaryCategory.getAnchor())) {
+            processCategoryAnchor(glossaryCategory, saved);
+        }
+
+        if (Objects.nonNull(glossaryCategory.getParentCategory())) {
+            processParentCategory(glossaryCategory, saved);
+        }
+
+        if 
(CollectionUtils.isNotEmpty(glossaryCategory.getChildrenCategories())) {
+            processCategoryChildren(glossaryCategory, saved);
+        }
+
+        saved = dataAccess.load(glossaryCategory);
+
+        setDisplayTextForRelations(glossaryCategory);
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.createCategory() : {}", saved);
+        }
+
+        return saved;
+    }
+
+    public List<AtlasGlossaryCategory> 
createCategories(List<AtlasGlossaryCategory> glossaryCategory) throws 
AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.createCategories({})", 
glossaryCategory);
+        }
+        if (Objects.isNull(glossaryCategory)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"glossaryCategory is null/empty");
+        }
+
+
+        List<AtlasGlossaryCategory> ret = new ArrayList<>();
+        for (AtlasGlossaryCategory category : glossaryCategory) {
+            ret.add(createCategory(category));
+        }
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.createCategories() : {}", ret);
+        }
+        return ret;
+    }
+
+    public AtlasGlossaryCategory updateCategory(AtlasGlossaryCategory 
glossaryCategory) throws AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.updateCategory({})", 
glossaryCategory);
+        }
+        if (Objects.isNull(glossaryCategory)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"GlossaryCategory is null/empty");
+        }
+
+        AtlasGlossaryCategory existing = dataAccess.load(glossaryCategory);
+        AtlasGlossaryCategory ret      = glossaryCategory;
+
+        if (!existing.equals(glossaryCategory)) {
+            try {
+                glossaryCategory.setGuid(existing.getGuid());
+                glossaryCategory.setQualifiedName(existing.getQualifiedName());
+
+                ret = dataAccess.save(glossaryCategory);
+            } catch (AtlasBaseException e) {
+                LOG.debug("No immediate attribute update. Exception: {}", 
e.getMessage());
+            } finally {
+                processRelations(glossaryCategory, existing);
+            }
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.updateCategory() : {}", ret);
+        }
+        ret = dataAccess.load(glossaryCategory);
+
+        setDisplayTextForRelations(glossaryCategory);
+
+        return ret;
+    }
+
+    public void deleteCategory(String categoryGuid) throws AtlasBaseException {
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.deleteCategory({})", categoryGuid);
+        }
+        if (Objects.isNull(categoryGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Category 
guid is null/empty");
+        }
+
+        dataAccess.delete(categoryGuid);
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.deleteCategory()");
+        }
+
+    }
+
+    public List<AtlasGlossaryTerm> getGlossaryTerms(String glossaryGuid, int 
offset, int limit, SortOrder sortOrder) throws AtlasBaseException {
+        if (Objects.isNull(glossaryGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"glossaryGuid is null/empty");
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getGlossaryTerms({}, {}, {}, {})", 
glossaryGuid, offset, limit, sortOrder);
+        }
+
+        AtlasGlossary glossary = getGlossary(glossaryGuid);
+
+        List<AtlasGlossaryTerm> ret;
+        if (CollectionUtils.isNotEmpty(glossary.getTerms())) {
+            List<AtlasGlossaryTerm> toLoad = glossary.getTerms().stream()
+                                                     .map(t -> 
getAtlasGlossaryTermSkeleton(t.getTermGuid()))
+                                                     
.collect(Collectors.toList());
+            Iterable<AtlasGlossaryTerm> terms = dataAccess.load(toLoad);
+            toLoad.clear();
+            terms.forEach(toLoad::add);
+            if (sortOrder != null) {
+                toLoad.sort((o1, o2) -> sortOrder == SortOrder.ASCENDING ?
+                                                
o1.getDisplayName().compareTo(o2.getDisplayName()) :
+                                                
o2.getDisplayName().compareTo(o1.getDisplayName()));
+            }
+            ret = new PaginationHelper<>(toLoad, offset, 
limit).getPaginatedList();
+        } else {
+            ret = Collections.emptyList();
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getGlossaryTerms() : {}", ret);
+        }
+
+        return ret;
+    }
+
+    public List<AtlasRelatedCategoryHeader> getGlossaryCategories(String 
glossaryGuid, int offset, int limit, SortOrder sortOrder) throws 
AtlasBaseException {
+        if (Objects.isNull(glossaryGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"glossaryGuid is null/empty");
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getGlossaryCategories({}, {}, {}, 
{})", glossaryGuid, offset, limit, sortOrder);
+        }
+
+        AtlasGlossary glossary = getGlossary(glossaryGuid);
+
+        List<AtlasRelatedCategoryHeader> ret;
+
+        List<AtlasRelatedCategoryHeader> categories = new 
ArrayList<>(glossary.getCategories());
+        if (CollectionUtils.isNotEmpty(categories)) {
+            if (sortOrder != null) {
+                categories.sort((o1, o2) -> sortOrder == SortOrder.ASCENDING ?
+                               
o1.getDisplayText().compareTo(o2.getDisplayText()) :
+                               
o2.getDisplayText().compareTo(o1.getDisplayText()));
+            }
+            ret = new PaginationHelper<>(categories, offset, 
limit).getPaginatedList();
+        } else {
+            ret = Collections.emptyList();
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getGlossaryCategories() : {}", ret);
+        }
+
+        return ret;
+    }
+
+    public List<AtlasRelatedTermHeader> getCategoryTerms(String categoryGuid, 
int offset, int limit, SortOrder sortOrder) throws AtlasBaseException {
+        if (Objects.isNull(categoryGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"categoryGuid is null/empty");
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getCategoryTerms({}, {}, {}, {})", 
categoryGuid, offset, limit, sortOrder);
+        }
+        AtlasGlossaryCategory glossaryCategory = getCategory(categoryGuid);
+
+        List<AtlasRelatedTermHeader> ret;
+        List<AtlasRelatedTermHeader> terms = new 
ArrayList<>(glossaryCategory.getTerms());
+        if (CollectionUtils.isNotEmpty(glossaryCategory.getTerms())) {
+            if (sortOrder != null) {
+                terms.sort((o1, o2) -> sortOrder == SortOrder.ASCENDING ?
+                                                    
o1.getDisplayText().compareTo(o2.getDisplayText()) :
+                                                    
o2.getDisplayText().compareTo(o1.getDisplayText()));
+            }
+            ret = new PaginationHelper<>(terms, offset, 
limit).getPaginatedList();
+        } else {
+            ret = Collections.emptyList();
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getCategoryTerms() : {}", ret);
+        }
+        return ret;
+    }
+
+    public Map<AtlasGlossaryTerm.Relation, Set<AtlasRelatedTermHeader>> 
getRelatedTerms(String termGuid, int offset, int limit, SortOrder sortOrder) 
throws AtlasBaseException {
+        if (Objects.isNull(termGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "termGuid 
is null/empty");
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getRelatedTerms({}, {}, {}, {})", 
termGuid, offset, limit, sortOrder);
+        }
+
+        AtlasGlossaryTerm                                            
glossaryTerm = getTerm(termGuid);
+        Map<AtlasGlossaryTerm.Relation, Set<AtlasRelatedTermHeader>> ret;
+        if (glossaryTerm.hasTerms()) {
+            ret = glossaryTerm.getRelatedTerms();
+        } else {
+            ret = Collections.emptyMap();
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getRelatedTerms() : {}", ret);
+        }
+
+        return ret;
+    }
+
+    public Map<String, List<AtlasRelatedCategoryHeader>> 
getRelatedCategories(String categoryGuid, int offset, int limit, SortOrder 
sortOrder) throws AtlasBaseException {
+        if (Objects.isNull(categoryGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"categoryGuid is null/empty");
+        }
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("==> GlossaryService.getRelatedCategories({}, {}, {}, 
{})", categoryGuid, offset, limit, sortOrder);
+        }
+
+        AtlasGlossaryCategory glossaryCategory = getCategory(categoryGuid);
+
+        Map<String, List<AtlasRelatedCategoryHeader>> ret = new HashMap<>();
+        if (glossaryCategory.getParentCategory() != null) {
+            ret.put("parent", new ArrayList<AtlasRelatedCategoryHeader>() {{
+                add(glossaryCategory.getParentCategory());
+            }});
+        }
+        if 
(CollectionUtils.isNotEmpty(glossaryCategory.getChildrenCategories())) {
+            ret.put("children", new 
ArrayList<>(glossaryCategory.getChildrenCategories()));
+        }
+
+
+        if (DEBUG_ENABLED) {
+            LOG.debug("<== GlossaryService.getRelatedCategories() : {}", ret);
+        }
+
+        return ret;
+    }
+
+    public List<AtlasEntityHeader> getAssignedEntities(final String termGuid, 
int offset, int limit, SortOrder sortOrder) throws AtlasBaseException {
+        if (Objects.isNull(termGuid)) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "termGuid 
is null/empty");
+        }
+
+        AtlasGlossaryTerm      glossaryTerm     = 
dataAccess.load(getAtlasGlossaryTermSkeleton(termGuid));
+        Set<AtlasEntityHeader> assignedEntities = 
glossaryTerm.getAssignedEntities();
+
+        List<AtlasEntityHeader> ret;
+        if (CollectionUtils.isNotEmpty(assignedEntities)) {
+            ret = new ArrayList<>(assignedEntities);
+            if (sortOrder != null) {
+                ret.sort((o1, o2) -> sortOrder == SortOrder.ASCENDING ?
+                                             
o1.getDisplayText().compareTo(o2.getDisplayText()) :
+                                             
o2.getDisplayText().compareTo(o1.getDisplayText()));
+            }
+            ret = new PaginationHelper<>(assignedEntities, offset, 
limit).getPaginatedList();
+        } else {
+            ret = Collections.emptyList();
+        }
+
+        return ret;
+    }
+
+    private static AtlasGlossary getGlossarySkeleton(String glossaryGuid) {
+        AtlasGlossary glossary = new AtlasGlossary();
+        glossary.setGuid(glossaryGuid);
+        return glossary;
+    }
+
+    private void processAssignedEntities(final AtlasGlossaryTerm newObj, final 
AtlasGlossaryTerm existing) throws AtlasBaseException {
+        if (newObj.equals(existing)) return;
+
+        if (CollectionUtils.isNotEmpty(newObj.getAssignedEntities())) {
+            for (AtlasEntityHeader entityHeader : 
newObj.getAssignedEntities()) {
+                createRelationship(defineTermAssignment(existing.getGuid(), 
entityHeader));
+            }
+        }
+    }
+
+    private void setDisplayTextForRelations(final AtlasGlossary ret) throws 
AtlasBaseException {
+        if (Objects.nonNull(ret.getTerms())) {
+            setDisplayNameForTerms(ret.getTerms());
+        }
+
+        if (Objects.nonNull(ret.getCategories())) {
+            setDisplayNameForRelatedCategories(ret.getCategories());
+        }
+    }
+
+    private void setDisplayTextForRelations(final AtlasGlossaryTerm ret) 
throws AtlasBaseException {
+        if (Objects.nonNull(ret.getCategories())) {
+            setDisplayNameForTermCategories(ret.getCategories());
+        }
+        if (Objects.nonNull(ret.getRelatedTerms())) {
+            for (Map.Entry<AtlasGlossaryTerm.Relation, 
Set<AtlasRelatedTermHeader>> entry : ret.getRelatedTerms().entrySet()) {
+                setDisplayNameForTerms(entry.getValue());
+            }
+        }
+    }
+
+    private void setDisplayTextForRelations(final AtlasGlossaryCategory 
glossaryCategory) throws AtlasBaseException {
+        if (Objects.nonNull(glossaryCategory.getChildrenCategories())) {
+            
setDisplayNameForRelatedCategories(glossaryCategory.getChildrenCategories());
+        }
+        if (Objects.nonNull(glossaryCategory.getTerms())) {
+            setDisplayNameForTerms(glossaryCategory.getTerms());
+        }
+    }
+
+    private void processRelations(final AtlasGlossaryTerm newObj, final 
AtlasGlossaryTerm existing) throws AtlasBaseException {
+        boolean hasRelatedTerms     = newObj.hasTerms();
+        boolean hasTermAnchor       = Objects.nonNull(newObj.getAnchor());
+        boolean hasCategories       = Objects.nonNull(newObj.getCategories());
+
+        if (hasTermAnchor) {
+            processTermAnchor(newObj, existing);
+        }
+        if (hasRelatedTerms) {
+            processRelatedTerms(newObj, existing);
+        }
+        if (hasCategories) {
+            processAssociatedCategories(newObj, existing);
+        }
+    }
+
+    private void processRelations(final AtlasGlossaryCategory newObj, final 
AtlasGlossaryCategory existing) throws AtlasBaseException {
+        boolean hasParent   = Objects.nonNull(newObj.getParentCategory());
+        boolean hasChildren = Objects.nonNull(newObj.getChildrenCategories());
+        boolean hasAnchor   = Objects.nonNull(newObj.getAnchor());
+        boolean hasTerms    = Objects.nonNull(newObj.getTerms());
+
+        if (hasAnchor) {
+            processCategoryAnchor(newObj, existing);
+        }
+        if (hasParent) {
+            processParentCategory(newObj, existing);
+        }
+        if (hasChildren) {
+            processCategoryChildren(newObj, existing);
+        }
+        if (hasTerms) {
+            processAssociatedTerms(newObj, existing);
+        }
+    }
+
+    private void setDisplayNameForTermCategories(final 
Set<AtlasTermCategorizationHeader> categorizationHeaders) throws 
AtlasBaseException {
+        List<AtlasGlossaryCategory> categories = categorizationHeaders
+                                                         .stream()
+                                                         .map(id -> 
getAtlasGlossaryCategorySkeleton(id.getCategoryGuid()))
+                                                         
.collect(Collectors.toList());
+        Map<String, AtlasGlossaryCategory> categoryMap = new HashMap<>();
+        dataAccess.load(categories).forEach(c -> categoryMap.put(c.getGuid(), 
c));
+        categorizationHeaders.forEach(c -> 
c.setDisplayText(categoryMap.get(c.getCategoryGuid()).getDisplayName()));
+    }
+
+    private void setDisplayNameForRelatedCategories(final 
Set<AtlasRelatedCategoryHeader> categoryHeaders) throws AtlasBaseException {
+        List<AtlasGlossaryCategory> categories = categoryHeaders
+                                                    .stream()
+                                                    .map(id -> 
getAtlasGlossaryCategorySkeleton(id.getCategoryGuid()))
+                                                    
.collect(Collectors.toList());
+        Map<String, AtlasGlossaryCategory> categoryMap = new HashMap<>();
+        dataAccess.load(categories).forEach(c -> categoryMap.put(c.getGuid(), 
c));
+        categoryHeaders.forEach(c -> 
c.setDisplayText(categoryMap.get(c.getCategoryGuid()).getDisplayName()));
+    }
+
+    private void setDisplayNameForTerms(final Set<AtlasRelatedTermHeader> 
termHeaders) throws AtlasBaseException {
+        List<AtlasGlossaryTerm> terms = termHeaders
+                                                .stream()
+                                                .map(id -> 
getAtlasGlossaryTermSkeleton(id.getTermGuid()))
+                                                .collect(Collectors.toList());
+        Map<String, AtlasGlossaryTerm> termMap = new HashMap<>();
+        dataAccess.load(terms).iterator().forEachRemaining(t -> 
termMap.put(t.getGuid(), t));
+
+        termHeaders.forEach(t -> 
t.setDisplayText(termMap.get(t.getTermGuid()).getDisplayName()));
+    }
+
+    private void processAssociatedCategories(final AtlasGlossaryTerm newObj, 
final AtlasGlossaryTerm existing) throws AtlasBaseException {
+        if (newObj.equals(existing)) return;
+
+        Set<AtlasTermCategorizationHeader> categories = newObj.getCategories();
+        if (Objects.nonNull(categories)) {
+            Set<AtlasTermCategorizationHeader> existingCategories = 
existing.getCategories();
+            for (AtlasTermCategorizationHeader category : categories) {
+                if (Objects.isNull(category.getCategoryGuid())) {
+                    throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"Linked category guid can't be empty");
+                } else {
+                    if (Objects.nonNull(existingCategories) && 
existingCategories.contains(category)) {
+                        if (DEBUG_ENABLED) {
+                            LOG.debug("Skipping linked category {}", 
category.getCategoryGuid());
+                        }
+                        continue;
+                    }
+                    if (DEBUG_ENABLED) {
+                        LOG.debug("Creating relation between term = {} and 
category = {}", existing.getGuid(), category.getCategoryGuid());
+                    }
+                    createRelationship(defineCategorizedTerm(category, 
existing.getGuid()));
+                }
+            }
+        }
+    }
+
+    private void processAssociatedTerms(final AtlasGlossaryCategory 
glossaryCategory, final AtlasGlossaryCategory existing) throws 
AtlasBaseException {
+        if (Objects.equals(glossaryCategory.getTerms(), existing.getTerms())) 
return;
+
+        for (AtlasRelatedTermHeader linkedTerm : glossaryCategory.getTerms()) {
+            if (Objects.isNull(linkedTerm.getTermGuid())) {
+                throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"Linked term guid can't be empty");
+            } else {
+                // Don't process existing child relation
+                Set<AtlasRelatedTermHeader> existingTerms = 
existing.getTerms();
+                if (Objects.nonNull(existingTerms) && 
existingTerms.contains(linkedTerm)) {
+                    if (DEBUG_ENABLED) {
+                        LOG.debug("Skipping linked term {}", 
linkedTerm.getTermGuid());
+                    }
+                    continue;
+                }
+
+                if (DEBUG_ENABLED) {
+                    LOG.debug("Creating relation between category = {} and 
term = {}", existing.getGuid(), linkedTerm.getTermGuid());
+                }
+                // TODO: Accept the relationship attributes as well
+                createRelationship(defineCategorizedTerm(existing.getGuid(), 
linkedTerm));
+            }
+        }
+    }
+
+    private void processTermAnchor(final AtlasGlossaryTerm glossaryTerm, final 
AtlasGlossaryTerm saved) throws AtlasBaseException {
+        if (Objects.isNull(glossaryTerm.getAnchor())) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"glossaryTerm anchor mandatory attribute");
+        }
+
+        if (Objects.equals(glossaryTerm.getAnchor(), saved.getAnchor())) 
return;
+
+        if (Objects.isNull(glossaryTerm.getAnchor().getGlossaryGuid())) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Anchor 
guid can't be empty");
+        } else {
+            if (DEBUG_ENABLED) {
+                LOG.debug("Creating relation between glossary = {} and term = 
{}", glossaryTerm.getAnchor().getGlossaryGuid(), saved.getGuid());
+            }
+            
createRelationship(defineTermAnchorRelation(glossaryTerm.getAnchor().getGlossaryGuid(),
 glossaryTerm.getGuid()));
+        }
+    }
+
+    private void processCategoryAnchor(final AtlasGlossaryCategory newObj, 
final AtlasGlossaryCategory existing) throws AtlasBaseException {
+        if (Objects.isNull(newObj.getAnchor())) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"glossaryCategory anchor mandatory attribute");
+        }
+
+        // Don't process anchor if no change
+        if (Objects.equals(newObj.getAnchor(), existing.getAnchor())) return;
+
+        if (Objects.isNull(newObj.getAnchor().getGlossaryGuid())) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Category 
anchor guid can't be empty");
+        } else {
+            if (DEBUG_ENABLED) {
+                LOG.debug("Creating relation between glossary = {} and 
category = {}", newObj.getAnchor().getGlossaryGuid(), existing.getGuid());
+            }
+            
createRelationship(defineCategoryAnchorRelation(newObj.getAnchor().getGlossaryGuid(),
 existing.getGuid()));
+        }
+    }
+
+    private void processRelatedTerms(final AtlasGlossaryTerm incomingObj, 
final AtlasGlossaryTerm savedObj) throws AtlasBaseException {
+        if (incomingObj.hasTerms()) {
+            for (Map.Entry<AtlasGlossaryTerm.Relation, 
Set<AtlasRelatedTermHeader>> entry : incomingObj.getRelatedTerms().entrySet()) {
+                AtlasGlossaryTerm.Relation  relation = entry.getKey();
+                Set<AtlasRelatedTermHeader> terms    = entry.getValue();
+                if (DEBUG_ENABLED) {
+                    LOG.debug("Creating relation {}", relation);
+                    LOG.debug("Related Term count = {}", terms.size());
+                }
+                if (Objects.nonNull(terms)) {
+                    for (AtlasRelatedTermHeader atlasGlossaryTerm : terms) {
+                        
createRelationship(defineTermRelation(relation.getRelationName(), 
savedObj.getGuid(), atlasGlossaryTerm));
+                    }
+                }
+            }
+        }
+        // TODO: Process other term relations as well
+    }
+
+    private void processCategoryChildren(final AtlasGlossaryCategory newObj, 
final AtlasGlossaryCategory existing) throws AtlasBaseException {
+        for (AtlasRelatedCategoryHeader childCategory : 
newObj.getChildrenCategories()) {
+            if (Objects.isNull(childCategory.getCategoryGuid())) {
+                throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, 
"Child category guid can't be empty");
+            } else {
+                // Don't process existing child relation
+                Set<AtlasRelatedCategoryHeader> existingChildren = 
existing.getChildrenCategories();
+                if (Objects.nonNull(existingChildren) && 
existingChildren.contains(childCategory)) {
+                    if (DEBUG_ENABLED) {
+                        LOG.debug("Skipping category child {}", 
childCategory.getCategoryGuid());
+                    }
+                    continue;
+                }
+
+                if (DEBUG_ENABLED) {
+                    LOG.debug("Creating relation between glossary = {} and 
term = {}", existing.getGuid(), childCategory.getCategoryGuid());
+                }
+                // TODO: Accept the relationship attributes as well
+                
createRelationship(defineCategoryHierarchyLink(existing.getGuid(), 
childCategory));
+            }
+        }
+    }
+
+    private void processParentCategory(final AtlasGlossaryCategory newObj, 
final AtlasGlossaryCategory existing) throws AtlasBaseException {
+        // Don't process unchanged parent
+        if (Objects.equals(newObj.getParentCategory(), 
existing.getParentCategory())) return;
+
+        if (Objects.isNull(newObj.getParentCategory().getCategoryGuid())) {
+            throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Parent 
category guid can't be empty");
+        } else {
+            if (DEBUG_ENABLED) {
+                LOG.debug("Creating category hierarchy b/w parent = {} and 
child = {}", newObj.getParentCategory().getCategoryGuid(), existing.getGuid());
+            }
+            
createRelationship(defineCategoryHierarchyLink(newObj.getParentCategory(), 
newObj.getGuid()));
+        }
+    }
+
+    private Map<String, List<AtlasGlossaryTerm>> loadTerms(final 
Map<AtlasGlossaryTerm.Relation, Set<AtlasRelatedTermHeader>> relatedTerms,
+                                                           final int offset,
+                                                           final int limit,
+                                                           final SortOrder 
sortOrder) throws AtlasBaseException {
+        Map<String, List<AtlasGlossaryTerm>> ret = new HashMap<>();
+
+        for (Map.Entry<AtlasGlossaryTerm.Relation, 
Set<AtlasRelatedTermHeader>> entry : relatedTerms.entrySet()) {
+            ret.put(entry.getKey().getRelationAttrName(), 
loadTerms(entry.getValue(), offset, limit, sortOrder));
+        }
+
+        return ret;
+    }
+
+    private List<AtlasGlossaryTerm> loadTerms(final 
Set<AtlasRelatedTermHeader> terms,
+                                              final int offset,
+                                              final int limit,
+                                              final SortOrder sortOrder) 
throws AtlasBaseException {
+        return loadTerms(new ArrayList<>(terms), offset, limit, sortOrder);
+    }
+
+    private List<AtlasGlossaryTerm> loadTerms(final 
List<AtlasRelatedTermHeader> terms,
+                                              final int offset,
+                                              final int limit,
+                                              final SortOrder sortOrder) 
throws AtlasBaseException {
+        Objects.requireNonNull(terms);
+        List<AtlasGlossaryTerm> ret;
+
+        ret = terms.stream().map(id -> 
getAtlasGlossaryTermSkeleton(id.getTermGuid())).collect(Collectors.toList());
+        Iterable<AtlasGlossaryTerm> loadedTerms = dataAccess.load(ret);
+        ret.clear();
+        loadedTerms.forEach(ret::add);
+        // Sort only when needed
+        if (sortOrder != null) {
+            ret.sort((o1, o2) -> sortOrder == SortOrder.ASCENDING ?
+                                         
o1.getDisplayName().compareTo(o2.getDisplayName()) :
+                                         
o2.getDisplayName().compareTo(o1.getDisplayName()));
+        }
+        return new PaginationHelper<>(ret, offset, limit).getPaginatedList();
+    }
+
+    private List<AtlasGlossaryCategory> loadCategories(final 
Set<AtlasRelatedCategoryHeader> categories,
+                                                       final int offset,
+                                                       final int limit,
+                                                       final SortOrder 
sortOrder) throws AtlasBaseException {
+        return loadCategories(new ArrayList<>(categories), offset, limit, 
sortOrder);
+    }
+
+    private List<AtlasGlossaryCategory> loadCategories(final 
List<AtlasRelatedCategoryHeader> categories,
+                                                       final int offset,
+                                                       final int limit,
+                                                       final SortOrder 
sortOrder) throws AtlasBaseException {
+        Objects.requireNonNull(categories);
+        List<AtlasGlossaryCategory> ret = categories.stream()
+                                                    .map(id -> 
getAtlasGlossaryCategorySkeleton(id.getCategoryGuid()))
+                                                    
.collect(Collectors.toList());
+        Iterable<AtlasGlossaryCategory> loadedCategories = 
dataAccess.load(ret);
+        ret.clear();
+        loadedCategories.forEach(ret::add);
+
+        // Sort only when needed
+        if (sortOrder != null) {
+            ret.sort((o1, o2) -> sortOrder == SortOrder.ASCENDING ?
+                                         
o1.getDisplayName().compareTo(o2.getDisplayName()) :
+                                         
o2.getDisplayName().compareTo(o1.getDisplayName()));
+        }
+
+        return new PaginationHelper<>(ret, offset, limit).getPaginatedList();
+    }
+
+    private AtlasGlossaryTerm getAtlasGlossaryTermSkeleton(final String 
termGuid) {
+        AtlasGlossaryTerm glossaryTerm = new AtlasGlossaryTerm();
+        glossaryTerm.setGuid(termGuid);
+        return glossaryTerm;
+    }
+
+    private AtlasGlossaryCategory getAtlasGlossaryCategorySkeleton(final 
String categoryGuid) {
+        AtlasGlossaryCategory glossaryCategory = new AtlasGlossaryCategory();
+        glossaryCategory.setGuid(categoryGuid);
+        return glossaryCategory;
+    }
+
+    private void createRelationship(AtlasRelationship relationship) throws 
AtlasBaseException {
+        try {
+            relationshipStore.create(relationship);
+        } catch (AtlasBaseException e) {
+            if 
(!e.getAtlasErrorCode().equals(AtlasErrorCode.RELATIONSHIP_ALREADY_EXISTS)) {
+                throw e;
+            }
+        }
+    }
+
+    private AtlasRelationship defineTermAnchorRelation(String glossaryGuid, 
String termGuid) {
+        return new AtlasRelationship(TERM_ANCHOR, new 
AtlasObjectId(glossaryGuid), new AtlasObjectId(termGuid));
+    }
+
+    private AtlasRelationship defineCategoryAnchorRelation(String 
glossaryGuid, String categoryGuid) {
+        return new AtlasRelationship(CATEGORY_ANCHOR, new 
AtlasObjectId(glossaryGuid), new AtlasObjectId(categoryGuid));
+    }
+
+    private AtlasRelationship defineCategoryHierarchyLink(String 
parentCategoryGuid, AtlasRelatedCategoryHeader childCategory) {
+        AtlasRelationship relationship = new 
AtlasRelationship(CATEGORY_HIERARCHY, new AtlasObjectId(parentCategoryGuid), 
new AtlasObjectId(childCategory.getCategoryGuid()));
+        relationship.setAttribute("description", 
childCategory.getDescription());
+        return relationship;
+    }
+
+    private AtlasRelationship defineCategoryHierarchyLink(final 
AtlasRelatedCategoryHeader parentCategory, final String childGuid) {
+        AtlasRelationship relationship = new 
AtlasRelationship(CATEGORY_HIERARCHY, new 
AtlasObjectId(parentCategory.getCategoryGuid()), new AtlasObjectId(childGuid));
+        relationship.setAttribute("description", 
parentCategory.getDescription());
+        return relationship;
+    }
+
+    private AtlasRelationship defineCategorizedTerm(String categoryGuid, 
AtlasRelatedTermHeader relatedTermId) {
+        AtlasRelationship relationship = new 
AtlasRelationship(TERM_CATEGORIZATION, new AtlasObjectId(categoryGuid), new 
AtlasObjectId(relatedTermId.getTermGuid()));
+
+        relationship.setAttribute("expression", relatedTermId.getExpression());
+        relationship.setAttribute("description", 
relatedTermId.getDescription());
+        relationship.setAttribute("steward", relatedTermId.getSteward());
+        relationship.setAttribute("source", relatedTermId.getSource());
+        if (Objects.nonNull(relatedTermId.getStatus())) {
+            relationship.setAttribute("status", 
relatedTermId.getStatus().name());
+        }
+
+        return relationship;
+    }
+
+    private AtlasRelationship 
defineCategorizedTerm(AtlasTermCategorizationHeader relatedCategoryId, String 
termId) {
+        AtlasRelationship relationship = new 
AtlasRelationship(TERM_CATEGORIZATION, new 
AtlasObjectId(relatedCategoryId.getCategoryGuid()), new AtlasObjectId(termId));
+
+        relationship.setAttribute("description", 
relatedCategoryId.getDescription());
+        if (Objects.nonNull(relatedCategoryId.getStatus())) {
+            relationship.setAttribute("status", 
relatedCategoryId.getStatus().name());
+        }
+
+        return relationship;
+    }
+
+    private AtlasRelationship defineTermRelation(String relation, String 
end1TermGuid, AtlasRelatedTermHeader end2RelatedTerm) {
+        AtlasRelationship relationship = new AtlasRelationship(relation, new 
AtlasObjectId(end1TermGuid), new AtlasObjectId(end2RelatedTerm.getTermGuid()));
+
+        relationship.setAttribute("expression", 
end2RelatedTerm.getExpression());
+        relationship.setAttribute("description", 
end2RelatedTerm.getDescription());
+        relationship.setAttribute("steward", end2RelatedTerm.getSteward());
+        relationship.setAttribute("source", end2RelatedTerm.getSource());
+        if (Objects.nonNull(end2RelatedTerm.getStatus())) {
+            relationship.setAttribute("status", 
end2RelatedTerm.getStatus().name());
+        }
+
+        return relationship;
+    }
+
+    private AtlasRelationship defineTermAssignment(String termGuid, 
AtlasEntityHeader entityHeader) {
+        return new AtlasRelationship(TERM_ASSIGNMENT, new 
AtlasObjectId(termGuid), new AtlasObjectId(entityHeader.getGuid()));
+    }
+
+    static class PaginationHelper<T> {
+        private int     pageStart;
+        private int     pageEnd;
+        private int     maxSize;
+        private List<T> items;
+
+        PaginationHelper(Collection<T> items, int offset, int limit) {
+            Objects.requireNonNull(items, "items can't be empty/null");
+            this.items = new ArrayList<>(items);
+            this.maxSize = items.size();
+
+            // If limit is negative then limit is effectively the maxSize, 
else the smaller one out of limit and maxSize
+            int adjustedLimit = limit < 0 ? maxSize : Integer.min(maxSize, 
limit);
+            // Page starting can only be between zero and adjustedLimit
+            pageStart = Integer.max(0, offset);
+            // Page end can't exceed the maxSize
+            pageEnd = Integer.min(adjustedLimit + pageStart, maxSize);
+        }
+
+        List<T> getPaginatedList() {
+            List<T> ret;
+            if (isValidOffset()) {
+                if (isPagingNeeded()) {
+                    ret = items.subList(pageStart, pageEnd);
+                } else {
+                    ret = items;
+                }
+            } else {
+                ret = Collections.emptyList();
+            }
+
+            return ret;
+        }
+
+        private boolean isPagingNeeded() {
+            return !(pageStart == 0 && pageEnd == maxSize) && pageStart <= 
pageEnd;
+        }
+
+        private boolean isValidOffset() {
+            return pageStart <= maxSize;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/75415862/repository/src/main/java/org/apache/atlas/repository/ogm/AbstractDataTransferObject.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/ogm/AbstractDataTransferObject.java
 
b/repository/src/main/java/org/apache/atlas/repository/ogm/AbstractDataTransferObject.java
index f1a8bc9..2afdfe1 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/ogm/AbstractDataTransferObject.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/ogm/AbstractDataTransferObject.java
@@ -27,14 +27,24 @@ import org.apache.commons.lang3.StringUtils;
 
 
 public abstract class AbstractDataTransferObject<T extends 
AtlasBaseModelObject> implements DataTransferObject<T> {
+
+    private static final String DEFAULT_PREFIX = "Atlas";
+
     private final AtlasTypeRegistry typeRegistry;
     private final Class<T>          objectType;
     private final String            entityTypeName;
+    private final String            alternateEntityTypeName;
 
-    protected AbstractDataTransferObject(AtlasTypeRegistry typeRegistry, 
Class<T> tClass) {
+    protected AbstractDataTransferObject(AtlasTypeRegistry typeRegistry, 
Class<T> tClass, boolean isInternal) {
         this.typeRegistry   = typeRegistry;
         this.objectType     = tClass;
-        this.entityTypeName = Constants.INTERNAL_PROPERTY_KEY_PREFIX + 
objectType.getSimpleName();
+        if (isInternal) {
+            this.entityTypeName = Constants.INTERNAL_PROPERTY_KEY_PREFIX + 
objectType.getSimpleName();
+            this.alternateEntityTypeName = null;
+        } else {
+            this.entityTypeName = objectType.getSimpleName();
+            this.alternateEntityTypeName = 
entityTypeName.startsWith(DEFAULT_PREFIX) ? 
entityTypeName.substring(DEFAULT_PREFIX.length()) : null;
+        }
     }
 
     @Override
@@ -44,7 +54,11 @@ public abstract class AbstractDataTransferObject<T extends 
AtlasBaseModelObject>
 
     @Override
     public AtlasEntityType getEntityType() {
-        return typeRegistry.getEntityTypeByName(entityTypeName);
+        AtlasEntityType ret = typeRegistry.getEntityTypeByName(entityTypeName);
+        if (ret == null) {
+            ret = typeRegistry.getEntityTypeByName(alternateEntityTypeName);
+        }
+        return ret;
     }
 
 

http://git-wip-us.apache.org/repos/asf/atlas/blob/75415862/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasSavedSearchDTO.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasSavedSearchDTO.java
 
b/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasSavedSearchDTO.java
deleted file mode 100644
index a1a8f59..0000000
--- 
a/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasSavedSearchDTO.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * 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.ogm;
-
-import org.apache.atlas.exception.AtlasBaseException;
-import org.apache.atlas.model.discovery.SearchParameters;
-import org.apache.atlas.model.instance.AtlasEntity;
-import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
-import org.apache.atlas.model.profile.AtlasUserSavedSearch;
-import org.apache.atlas.type.AtlasType;
-import org.apache.atlas.type.AtlasTypeRegistry;
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.HashMap;
-import java.util.Map;
-
-
-public class AtlasSavedSearchDTO extends 
AbstractDataTransferObject<AtlasUserSavedSearch> {
-    private static final String PROPERTY_NAME              = "name";
-    private static final String PROPERTY_OWNER_NAME        = "ownerName";
-    private static final String PROPERTY_SEARCH_PARAMETERS = 
"searchParameters";
-    private static final String PROPERTY_UNIQUE_NAME       = "uniqueName";
-    private static final String PROPERTY_SEARCH_TYPE       = "searchType";
-    private static final String PROPERTY_UI_PARAMETERS       = "uiParameters";
-
-    public AtlasSavedSearchDTO(AtlasTypeRegistry typeRegistry) {
-        super(typeRegistry, AtlasUserSavedSearch.class);
-    }
-
-    @Override
-    public AtlasUserSavedSearch from(AtlasEntity entity) {
-        AtlasUserSavedSearch savedSearch = new AtlasUserSavedSearch();
-
-        savedSearch.setGuid(entity.getGuid());
-        savedSearch.setName((String) entity.getAttribute(PROPERTY_NAME));
-        savedSearch.setOwnerName((String) 
entity.getAttribute(PROPERTY_OWNER_NAME));
-        
savedSearch.setSearchType(AtlasUserSavedSearch.SavedSearchType.to((String) 
entity.getAttribute(PROPERTY_SEARCH_TYPE)));
-
-        String jsonSearchParams = (String) 
entity.getAttribute(PROPERTY_SEARCH_PARAMETERS);
-
-        if (StringUtils.isNotEmpty(jsonSearchParams)) {
-            
savedSearch.setSearchParameters(AtlasType.fromJson(jsonSearchParams, 
SearchParameters.class));
-        }
-
-        savedSearch.setUiParameters((String) 
entity.getAttribute(PROPERTY_UI_PARAMETERS));
-
-        return savedSearch;
-    }
-
-    @Override
-    public AtlasUserSavedSearch from(AtlasEntityWithExtInfo entityWithExtInfo) 
{
-        return from(entityWithExtInfo.getEntity());
-    }
-
-    @Override
-    public AtlasEntity toEntity(AtlasUserSavedSearch obj) throws 
AtlasBaseException {
-        AtlasEntity entity = getDefaultAtlasEntity(obj);
-
-        entity.setAttribute(PROPERTY_NAME, obj.getName());
-        entity.setAttribute(PROPERTY_OWNER_NAME, obj.getOwnerName());
-        entity.setAttribute(PROPERTY_SEARCH_TYPE, obj.getSearchType());
-        entity.setAttribute(PROPERTY_UNIQUE_NAME, getUniqueValue(obj));
-
-        if (obj.getSearchParameters() != null) {
-            entity.setAttribute(PROPERTY_SEARCH_PARAMETERS, 
AtlasType.toJson(obj.getSearchParameters()));
-        }
-
-        entity.setAttribute(PROPERTY_UI_PARAMETERS, obj.getUiParameters());
-
-        return entity;
-    }
-
-    @Override
-    public AtlasEntityWithExtInfo toEntityWithExtInfo(AtlasUserSavedSearch 
obj) throws AtlasBaseException {
-        return new AtlasEntityWithExtInfo(toEntity(obj));
-    }
-
-    @Override
-    public Map<String, Object> getUniqueAttributes(AtlasUserSavedSearch obj) {
-        Map<String, Object> ret = new HashMap<>();
-
-        ret.put(PROPERTY_UNIQUE_NAME, getUniqueValue(obj));
-
-        return ret;
-    }
-
-    private String getUniqueValue(AtlasUserSavedSearch obj) {
-        return String.format("%s:%s", obj.getOwnerName(), obj.getName()) ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/atlas/blob/75415862/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasUserProfileDTO.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasUserProfileDTO.java
 
b/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasUserProfileDTO.java
deleted file mode 100644
index bcf2b9d..0000000
--- 
a/repository/src/main/java/org/apache/atlas/repository/ogm/AtlasUserProfileDTO.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * 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.ogm;
-
-import org.apache.atlas.exception.AtlasBaseException;
-import org.apache.atlas.model.instance.AtlasEntity;
-import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
-import org.apache.atlas.model.instance.AtlasObjectId;
-import org.apache.atlas.model.profile.AtlasUserProfile;
-import org.apache.atlas.model.profile.AtlasUserSavedSearch;
-import org.apache.atlas.type.AtlasTypeRegistry;
-
-import java.util.*;
-
-public class AtlasUserProfileDTO extends 
AbstractDataTransferObject<AtlasUserProfile> {
-    private final String PROPERTY_USER_NAME      = "name";
-    private final String PROPERTY_FULL_NAME      = "fullName";
-    private final String PROPERTY_SAVED_SEARCHES = "savedSearches";
-
-    private final AtlasSavedSearchDTO savedSearchDTO;
-
-    public AtlasUserProfileDTO(AtlasTypeRegistry typeRegistry, 
AtlasSavedSearchDTO savedSearchDTO) {
-        super(typeRegistry, AtlasUserProfile.class);
-
-        this.savedSearchDTO = savedSearchDTO;
-    }
-
-    public AtlasUserProfile from(AtlasEntity entity) {
-        AtlasUserProfile profile = new AtlasUserProfile();
-
-        profile.setGuid(entity.getGuid());
-        profile.setName((String) entity.getAttribute(PROPERTY_USER_NAME));
-        profile.setFullName((String) entity.getAttribute(PROPERTY_FULL_NAME));
-
-        return profile;
-    }
-
-    public AtlasUserProfile from(AtlasEntityWithExtInfo entityWithExtInfo) {
-        AtlasUserProfile userProfile = from(entityWithExtInfo.getEntity());
-
-        Object savedSearches = 
entityWithExtInfo.getEntity().getAttribute(PROPERTY_SAVED_SEARCHES);
-
-        if (savedSearches instanceof Collection) {
-            for (Object o : (Collection) savedSearches) {
-                if (o instanceof AtlasObjectId) {
-                    AtlasObjectId ssObjId  = (AtlasObjectId) o;
-                    AtlasEntity   ssEntity = 
entityWithExtInfo.getReferredEntity(ssObjId.getGuid());
-
-                    if (ssEntity != null && ssEntity.getStatus() == 
AtlasEntity.Status.ACTIVE) {
-                        AtlasUserSavedSearch savedSearch = 
savedSearchDTO.from(ssEntity);
-                        userProfile.getSavedSearches().add(savedSearch);
-                    }
-                }
-            }
-        }
-
-        return userProfile;
-    }
-
-    @Override
-    public AtlasEntity toEntity(AtlasUserProfile obj) throws 
AtlasBaseException {
-        AtlasEntity entity = getDefaultAtlasEntity(obj);
-
-        entity.setAttribute(PROPERTY_USER_NAME, obj.getName());
-        entity.setAttribute(PROPERTY_FULL_NAME, obj.getFullName());
-
-        return entity;
-    }
-
-    @Override
-    public AtlasEntityWithExtInfo toEntityWithExtInfo(AtlasUserProfile obj) 
throws AtlasBaseException {
-        AtlasEntity            entity            = toEntity(obj);
-        AtlasEntityWithExtInfo entityWithExtInfo = new 
AtlasEntityWithExtInfo(entity);
-
-        List<AtlasObjectId> objectIds = new ArrayList<>();
-
-        for (AtlasUserSavedSearch ss : obj.getSavedSearches()) {
-            AtlasEntity ssEntity = savedSearchDTO.toEntity(ss);
-
-            entityWithExtInfo.addReferredEntity(ssEntity);
-
-            objectIds.add(new AtlasObjectId(ssEntity.getGuid(), 
savedSearchDTO.getEntityType().getTypeName(), 
savedSearchDTO.getUniqueAttributes(ss)));
-        }
-
-        if (objectIds.size() > 0) {
-            entity.setAttribute(PROPERTY_SAVED_SEARCHES, objectIds);
-        }
-
-        return entityWithExtInfo;
-    }
-
-    @Override
-    public Map<String, Object> getUniqueAttributes(AtlasUserProfile obj) {
-        Map<String, Object> ret = new HashMap<>();
-
-        ret.put(PROPERTY_USER_NAME, obj.getName());
-
-        return ret;
-    }
-}

http://git-wip-us.apache.org/repos/asf/atlas/blob/75415862/repository/src/main/java/org/apache/atlas/repository/ogm/DTORegistry.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/ogm/DTORegistry.java 
b/repository/src/main/java/org/apache/atlas/repository/ogm/DTORegistry.java
index 818960d..43f4a60 100644
--- a/repository/src/main/java/org/apache/atlas/repository/ogm/DTORegistry.java
+++ b/repository/src/main/java/org/apache/atlas/repository/ogm/DTORegistry.java
@@ -17,29 +17,30 @@
  */
 package org.apache.atlas.repository.ogm;
 
-import org.apache.atlas.type.AtlasTypeRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import javax.inject.Inject;
-import java.lang.reflect.Type;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 @Component
 public class DTORegistry {
-    private final Map<Type, DataTransferObject> typeDTOMap = new HashMap<>();
+    private static final Logger LOG = 
LoggerFactory.getLogger(DTORegistry.class);
 
+    private final Map<Class, DataTransferObject> typeDTOMap = new HashMap<>();
 
     @Inject
-    public DTORegistry(AtlasTypeRegistry typeRegistry) {
-        AtlasSavedSearchDTO savedSearchDTO = new 
AtlasSavedSearchDTO(typeRegistry);
-        AtlasUserProfileDTO userProfileDTO = new 
AtlasUserProfileDTO(typeRegistry, savedSearchDTO);
-
-        registerDTO(savedSearchDTO);
-        registerDTO(userProfileDTO);
+    public DTORegistry(Set<DataTransferObject> availableDTOs) {
+        for (DataTransferObject availableDTO : availableDTOs) {
+            LOG.info("Registering DTO: {}", 
availableDTO.getClass().getSimpleName());
+            registerDTO(availableDTO);
+        }
     }
 
-    public <T extends DataTransferObject> DataTransferObject get(Type t) {
+    public <T extends DataTransferObject> DataTransferObject get(Class t) {
         return typeDTOMap.get(t);
     }
 

http://git-wip-us.apache.org/repos/asf/atlas/blob/75415862/repository/src/main/java/org/apache/atlas/repository/ogm/DataAccess.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/ogm/DataAccess.java 
b/repository/src/main/java/org/apache/atlas/repository/ogm/DataAccess.java
index c99d2f8..e00c3e9 100644
--- a/repository/src/main/java/org/apache/atlas/repository/ogm/DataAccess.java
+++ b/repository/src/main/java/org/apache/atlas/repository/ogm/DataAccess.java
@@ -20,18 +20,29 @@ package org.apache.atlas.repository.ogm;
 import org.apache.atlas.AtlasErrorCode;
 import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.model.AtlasBaseModelObject;
+import org.apache.atlas.model.instance.AtlasEntity;
 import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
 import org.apache.atlas.model.instance.EntityMutationResponse;
 import org.apache.atlas.repository.store.graph.AtlasEntityStore;
 import org.apache.atlas.repository.store.graph.v1.AtlasEntityStream;
+import org.apache.atlas.utils.AtlasPerfTracer;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 import javax.inject.Inject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
 
 
 @Component
 public class DataAccess {
+    private static final Logger LOG      = 
LoggerFactory.getLogger(DataAccess.class);
+    private static final Logger PERF_LOG = 
AtlasPerfTracer.getPerfLogger("repository.DataAccess");
+
     private final AtlasEntityStore entityStore;
     private final DTORegistry      dtoRegistry;
 
@@ -42,49 +53,186 @@ public class DataAccess {
     }
 
     public <T extends AtlasBaseModelObject> T save(T obj) throws 
AtlasBaseException {
-        DataTransferObject<T> dto = 
(DataTransferObject<T>)dtoRegistry.get(obj.getClass());
+        Objects.requireNonNull(obj, "Can't save a null object");
+
+        AtlasPerfTracer perf = null;
+
+        try {
+            if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"DataAccess.save()");
+            }
+
+            DataTransferObject<T> dto = (DataTransferObject<T>) 
dtoRegistry.get(obj.getClass());
+
+            AtlasEntityWithExtInfo entityWithExtInfo      = 
dto.toEntityWithExtInfo(obj);
+            EntityMutationResponse entityMutationResponse = 
entityStore.createOrUpdate(new AtlasEntityStream(entityWithExtInfo), false);
+
+            if (noEntityMutation(entityMutationResponse)) {
+                throw new 
AtlasBaseException(AtlasErrorCode.DATA_ACCESS_SAVE_FAILED, obj.toString());
+            }
 
-        AtlasEntityWithExtInfo entityWithExtInfo      = 
dto.toEntityWithExtInfo(obj);
-        EntityMutationResponse entityMutationResponse = 
entityStore.createOrUpdate(new AtlasEntityStream(entityWithExtInfo), false);
+            // Since mutation context has guid information, attempt to set the 
same.
+            if (entityMutationResponse.getFirstEntityCreated() != null) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Object created");
+                }
+                
obj.setGuid(entityMutationResponse.getFirstEntityCreated().getGuid());
+            } else if (entityMutationResponse.getFirstEntityUpdated() != null) 
{
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Object updated");
+                }
+                
obj.setGuid(entityMutationResponse.getFirstEntityUpdated().getGuid());
+            }
 
-        if (hasError(entityMutationResponse)) {
-            throw new 
AtlasBaseException(AtlasErrorCode.DATA_ACCESS_SAVE_FAILED, obj.toString());
+            return this.load(obj);
+
+        } finally {
+            AtlasPerfTracer.log(perf);
         }
 
-        return this.load(obj);
+    }
+
+    public <T extends AtlasBaseModelObject> Iterable<T> save(Iterable<T> obj) 
throws AtlasBaseException {
+        Objects.requireNonNull(obj, "Can't save a null object");
+
+        AtlasPerfTracer perf = null;
+
+        try {
+            if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"DataAccess.multiSave()");
+            }
+
+            List<T> ret = new ArrayList<>();
+            for (T o : obj) {
+                ret.add(save(o));
+            }
+            return ret;
+
+        } finally {
+            AtlasPerfTracer.log(perf);
+        }
+    }
+
+    public <T extends AtlasBaseModelObject> Iterable<T> load(final Iterable<T> 
objects) throws AtlasBaseException {
+        Objects.requireNonNull(objects, "Objects to load");
+
+        AtlasPerfTracer perf = null;
+
+        try {
+            if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"DataAccess.multiLoad()");
+            }
+
+            List<AtlasBaseModelObject> ret = new ArrayList<>();
+
+            for (T object : objects) {
+                ret.add(load(object));
+            }
+
+            return (Iterable<T>) ret;
+
+        } finally {
+            AtlasPerfTracer.log(perf);
+        }
     }
 
     public <T extends AtlasBaseModelObject> T load(T obj) throws 
AtlasBaseException {
-        DataTransferObject<T>  dto = 
(DataTransferObject<T>)dtoRegistry.get(obj.getClass());
+        return load(obj, false);
+    }
+
+    public <T extends AtlasBaseModelObject> T load(T obj, boolean loadDeleted) 
throws AtlasBaseException {
+        Objects.requireNonNull(obj, "Can't load a null object");
+
+        AtlasPerfTracer perf = null;
+
+        try {
+            if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"DataAccess.load()");
+            }
+
+            DataTransferObject<T> dto = (DataTransferObject<T>) 
dtoRegistry.get(obj.getClass());
+
+            AtlasEntityWithExtInfo entityWithExtInfo;
 
-        AtlasEntityWithExtInfo entityWithExtInfo;
+            if (StringUtils.isNotEmpty(obj.getGuid())) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Load using GUID");
+                }
+                entityWithExtInfo = entityStore.getById(obj.getGuid());
+            } else {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Load using unique attributes");
+                }
+                entityWithExtInfo = 
entityStore.getByUniqueAttributes(dto.getEntityType(), 
dto.getUniqueAttributes(obj));
+            }
 
-        if (StringUtils.isNotEmpty(obj.getGuid())) {
-            entityWithExtInfo = entityStore.getById(obj.getGuid());
-        } else {
-            entityWithExtInfo = 
entityStore.getByUniqueAttributes(dto.getEntityType(), 
dto.getUniqueAttributes(obj));
+            if (!loadDeleted && entityWithExtInfo.getEntity().getStatus() == 
AtlasEntity.Status.DELETED) {
+                throw new 
AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_DELETED, obj.getGuid());
+            }
+
+            return dto.from(entityWithExtInfo);
+
+        } finally {
+            AtlasPerfTracer.log(perf);
         }
 
-        return dto.from(entityWithExtInfo);
     }
+    public void delete(String guid) throws AtlasBaseException {
+        Objects.requireNonNull(guid, "guid");
+        AtlasPerfTracer perf = null;
+
+        try {
+            if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"DataAccess.delete()");
+            }
+
+            entityStore.deleteById(guid);
+
+        } finally {
+            AtlasPerfTracer.log(perf);
+        }
+    }
+
+    public void delete(List<String> guids) throws AtlasBaseException {
+        Objects.requireNonNull(guids, "guids");
+
+        AtlasPerfTracer perf = null;
+
+        try {
+            if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"DataAccess.multiDelete()");
+            }
+
+            entityStore.deleteByIds(guids);
 
-    public void deleteUsingGuid(String guid) throws AtlasBaseException {
-        entityStore.deleteById(guid);
+        } finally {
+            AtlasPerfTracer.log(perf);
+        }
     }
 
     public <T extends AtlasBaseModelObject> void delete(T obj) throws 
AtlasBaseException {
-        T object = load(obj);
+        Objects.requireNonNull(obj, "Can't delete a null object");
+
+        AtlasPerfTracer perf = null;
+
+        try {
+            if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"DataAccess.delete()");
+            }
+
+            T object = load(obj);
+
+            if (object != null) {
+                delete(object.getGuid());
+            }
 
-        if (object != null) {
-            deleteUsingGuid(object.getGuid());
+        } finally {
+            AtlasPerfTracer.log(perf);
         }
     }
 
-    private boolean hasError(EntityMutationResponse er) {
-        return (er == null ||
-                !((er.getCreatedEntities() != null && 
er.getCreatedEntities().size() > 0)
-                        || (er.getUpdatedEntities() != null && 
er.getUpdatedEntities().size() > 0)
-                )
-        );
+    // Helper functions
+    private boolean noEntityMutation(EntityMutationResponse er) {
+        return er == null || (CollectionUtils.isEmpty(er.getCreatedEntities()) 
&& CollectionUtils.isEmpty(er.getUpdatedEntities()));
     }
 }

http://git-wip-us.apache.org/repos/asf/atlas/blob/75415862/repository/src/main/java/org/apache/atlas/repository/ogm/glossary/AbstractGlossaryDTO.java
----------------------------------------------------------------------
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/ogm/glossary/AbstractGlossaryDTO.java
 
b/repository/src/main/java/org/apache/atlas/repository/ogm/glossary/AbstractGlossaryDTO.java
new file mode 100644
index 0000000..722a463
--- /dev/null
+++ 
b/repository/src/main/java/org/apache/atlas/repository/ogm/glossary/AbstractGlossaryDTO.java
@@ -0,0 +1,163 @@
+/**
+ * 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.ogm.glossary;
+
+import org.apache.atlas.model.AtlasBaseModelObject;
+import org.apache.atlas.model.glossary.enums.AtlasTermRelationshipStatus;
+import org.apache.atlas.model.glossary.relations.AtlasGlossaryHeader;
+import org.apache.atlas.model.glossary.relations.AtlasRelatedCategoryHeader;
+import org.apache.atlas.model.glossary.relations.AtlasRelatedTermHeader;
+import org.apache.atlas.model.glossary.relations.AtlasTermCategorizationHeader;
+import org.apache.atlas.model.instance.AtlasRelatedObjectId;
+import org.apache.atlas.model.instance.AtlasRelationship;
+import org.apache.atlas.model.instance.AtlasStruct;
+import org.apache.atlas.repository.ogm.AbstractDataTransferObject;
+import org.apache.atlas.type.AtlasTypeRegistry;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class AbstractGlossaryDTO<T extends AtlasBaseModelObject> 
extends AbstractDataTransferObject<T> {
+    protected AbstractGlossaryDTO(final AtlasTypeRegistry typeRegistry, final 
Class<T> tClass, final boolean isInternal) {
+        super(typeRegistry, tClass, isInternal);
+    }
+
+    protected AtlasRelatedTermHeader 
constructRelatedTermId(AtlasRelatedObjectId relatedObjectId) {
+        AtlasRelatedTermHeader ret = new AtlasRelatedTermHeader();
+
+        ret.setTermGuid(relatedObjectId.getGuid());
+        ret.setRelationGuid(relatedObjectId.getRelationshipGuid());
+
+        AtlasStruct relationshipAttributes = 
relatedObjectId.getRelationshipAttributes();
+        if (relationshipAttributes != null) {
+            ret.setDescription((String) 
relationshipAttributes.getAttribute("description"));
+            ret.setExpression((String) 
relationshipAttributes.getAttribute("expression"));
+            ret.setSource((String) 
relationshipAttributes.getAttribute("source"));
+            ret.setSteward((String) 
relationshipAttributes.getAttribute("steward"));
+            Object status = relationshipAttributes.getAttribute("status");
+            if (status instanceof String) {
+                ret.setStatus(AtlasTermRelationshipStatus.valueOf((String) 
status));
+            } else if (status instanceof AtlasTermRelationshipStatus) {
+                ret.setStatus((AtlasTermRelationshipStatus) status);
+            }
+        }
+
+        return ret;
+    }
+
+    protected AtlasRelatedObjectId 
termIdToRelatedObjectId(AtlasRelatedTermHeader relatedTermId) {
+        AtlasRelatedObjectId ret = new AtlasRelatedObjectId();
+
+        ret.setGuid(relatedTermId.getTermGuid());
+        ret.setRelationshipGuid(relatedTermId.getRelationGuid());
+
+        AtlasStruct relationshipAttributes = new AtlasStruct();
+        relationshipAttributes.setAttribute("description", 
relatedTermId.getDescription());
+        relationshipAttributes.setAttribute("expression", 
relatedTermId.getExpression());
+        relationshipAttributes.setAttribute("source", 
relatedTermId.getSource());
+        relationshipAttributes.setAttribute("steward", 
relatedTermId.getSteward());
+        relationshipAttributes.setAttribute("status", 
relatedTermId.getStatus().name());
+
+        ret.setRelationshipAttributes(relationshipAttributes);
+
+        return ret;
+    }
+
+    protected AtlasRelatedCategoryHeader 
constructRelatedCategoryId(AtlasRelatedObjectId relatedObjectId) {
+        AtlasRelatedCategoryHeader ret = new AtlasRelatedCategoryHeader();
+
+        ret.setCategoryGuid(relatedObjectId.getGuid());
+        ret.setRelationGuid(relatedObjectId.getRelationshipGuid());
+
+        AtlasStruct relationshipAttributes = 
relatedObjectId.getRelationshipAttributes();
+        if (relationshipAttributes != null) {
+            ret.setDescription((String) 
relationshipAttributes.getAttribute("description"));
+        }
+
+        return ret;
+    }
+
+    protected AtlasRelatedObjectId 
relatedCategoryIdToRelatedObjectId(AtlasRelatedCategoryHeader 
relatedCategoryId) {
+        AtlasRelatedObjectId ret = new AtlasRelatedObjectId();
+
+        ret.setGuid(relatedCategoryId.getCategoryGuid());
+        ret.setRelationshipGuid(relatedCategoryId.getRelationGuid());
+
+        AtlasStruct relationshipAttributes = new AtlasStruct();
+        relationshipAttributes.setAttribute("description", 
relatedCategoryId.getDescription());
+        ret.setRelationshipAttributes(relationshipAttributes);
+
+        return ret;
+    }
+
+    protected AtlasGlossaryHeader constructGlossaryId(AtlasRelatedObjectId 
relatedObjectId) {
+        AtlasGlossaryHeader ret = new AtlasGlossaryHeader();
+
+        ret.setGlossaryGuid(relatedObjectId.getGuid());
+        ret.setRelationGuid(relatedObjectId.getRelationshipGuid());
+
+        return ret;
+    }
+
+    protected AtlasRelatedObjectId 
glossaryIdToRelatedObjectId(AtlasGlossaryHeader glossaryId) {
+        AtlasRelatedObjectId ret = new AtlasRelatedObjectId();
+
+        ret.setGuid(glossaryId.getGlossaryGuid());
+        ret.setRelationshipGuid(glossaryId.getRelationGuid());
+
+        return ret;
+    }
+
+    protected AtlasTermCategorizationHeader 
constructTermCategorizationId(final AtlasRelatedObjectId category) {
+        AtlasTermCategorizationHeader ret = new 
AtlasTermCategorizationHeader();
+
+        ret.setCategoryGuid(category.getGuid());
+        ret.setRelationGuid(category.getRelationshipGuid());
+
+        AtlasStruct relationshipAttributes = 
category.getRelationshipAttributes();
+        if (relationshipAttributes != null) {
+            ret.setDescription((String) 
relationshipAttributes.getAttribute("description"));
+            Object status = relationshipAttributes.getAttribute("status");
+            if (status instanceof AtlasTermRelationshipStatus) {
+                ret.setStatus((AtlasTermRelationshipStatus) status);
+            } else if (status instanceof String) {
+                ret.setStatus(AtlasTermRelationshipStatus.valueOf((String) 
status));
+            }
+        }
+
+        return ret;
+    }
+
+    protected Set<AtlasRelatedTermHeader> toRelatedTermIdsSet(Object 
relatedObjectIds) {
+        Set<AtlasRelatedTermHeader> ret = null;
+
+        if (relatedObjectIds instanceof Collection) {
+            ret = new HashSet<>();
+            for (Object t : (Collection) relatedObjectIds) {
+                if (t instanceof AtlasRelatedObjectId) {
+                    if (((AtlasRelatedObjectId) t).getRelationshipStatus() == 
AtlasRelationship.Status.ACTIVE) {
+                        ret.add(constructRelatedTermId((AtlasRelatedObjectId) 
t));
+                    }
+                }
+            }
+        }
+
+        return ret;
+    }
+}

Reply via email to