Repository: incubator-atlas Updated Branches: refs/heads/master 440bd2ae4 -> 5f248157b
ATLAS-683 Refactor local type-system cache with cache provider interface (vmadugun via shwethags) Project: http://git-wip-us.apache.org/repos/asf/incubator-atlas/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-atlas/commit/5f248157 Tree: http://git-wip-us.apache.org/repos/asf/incubator-atlas/tree/5f248157 Diff: http://git-wip-us.apache.org/repos/asf/incubator-atlas/diff/5f248157 Branch: refs/heads/master Commit: 5f248157bcb7daec1db66afe8d05f52f791296bc Parents: 440bd2a Author: Shwetha GS <[email protected]> Authored: Fri May 20 11:44:07 2016 +0530 Committer: Shwetha GS <[email protected]> Committed: Fri May 20 11:47:18 2016 +0530 ---------------------------------------------------------------------- .gitignore | 2 + .../org/apache/atlas/ApplicationProperties.java | 15 +- distro/src/conf/atlas-application.properties | 5 + release-log.txt | 1 + .../apache/atlas/RepositoryMetadataModule.java | 8 +- .../atlas/typesystem/types/TypeSystem.java | 200 ++++++---- .../types/cache/DefaultTypeCacheProvider.java | 229 +++++++++++ .../types/cache/ITypeCacheProvider.java | 137 +++++++ .../apache/atlas/ApplicationPropertiesTest.java | 14 +- .../atlas/typesystem/types/TypeSystemTest.java | 2 +- .../cache/DefaultTypeCacheProviderTest.java | 383 +++++++++++++++++++ 11 files changed, 912 insertions(+), 84 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore index f5899e2..0086a69 100755 --- a/.gitignore +++ b/.gitignore @@ -60,7 +60,9 @@ distro/hbase/*.tar.gz #solr package downloaded distro/solr/*.tgz +# Scala-IDE specific .cache-main +.cache-tests # emacs files *# http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/common/src/main/java/org/apache/atlas/ApplicationProperties.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/atlas/ApplicationProperties.java b/common/src/main/java/org/apache/atlas/ApplicationProperties.java index 6a4dca3..877d7db 100644 --- a/common/src/main/java/org/apache/atlas/ApplicationProperties.java +++ b/common/src/main/java/org/apache/atlas/ApplicationProperties.java @@ -92,13 +92,22 @@ public final class ApplicationProperties extends PropertiesConfiguration { return inConf.subset(prefix); } - public static Class getClass(String propertyName, String defaultValue) { + public static Class getClass(String propertyName, String defaultValue, Class assignableClass) + throws AtlasException { try { Configuration configuration = get(); String propertyValue = configuration.getString(propertyName, defaultValue); - return Class.forName(propertyValue); + Class<?> clazz = Class.forName(propertyValue); + if (assignableClass == null || assignableClass.isAssignableFrom(clazz)) { + return clazz; + } else { + String message = "Class " + clazz.getName() + " specified in property " + propertyName + + " is not assignable to class " + assignableClass.getName(); + LOG.error(message); + throw new AtlasException(message); + } } catch (Exception e) { - throw new RuntimeException(e); + throw new AtlasException(e); } } } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/distro/src/conf/atlas-application.properties ---------------------------------------------------------------------- diff --git a/distro/src/conf/atlas-application.properties b/distro/src/conf/atlas-application.properties index d4722fb..e2082ef 100755 --- a/distro/src/conf/atlas-application.properties +++ b/distro/src/conf/atlas-application.properties @@ -122,3 +122,8 @@ atlas.login.credentials.file=${sys:atlas.home}/conf/users-credentials.properties #########POLICY FILE PATH ######### atlas.auth.policy.file=${sys:atlas.home}/conf/policy-store.txt +######### Type Cache Provider Implementation ######## +# A type cache provider class which implements +# org.apache.atlas.typesystem.types.cache.ITypeCacheProvider. +# The default is DefaultTypeCacheProvider which is a local in-memory type cache. +#atlas.typesystem.cache.provider= http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/release-log.txt ---------------------------------------------------------------------- diff --git a/release-log.txt b/release-log.txt index c44ad0b..4a9a34d 100644 --- a/release-log.txt +++ b/release-log.txt @@ -21,6 +21,7 @@ ATLAS-409 Atlas will not import avro tables with schema read from a file (dosset ATLAS-379 Create sqoop and falcon metadata addons (venkatnrangan,bvellanki,sowmyaramesh via shwethags) ALL CHANGES: +ATLAS-683 Refactor local type-system cache with cache provider interface (vmadugun via shwethags) ATLAS-802 New look UI to show Business Catalog functionalities (kevalbhatt18 via yhemanth) ATLAS-658 Improve Lineage with Backbone porting (kevalbhatt18 via yhemanth) ATLAS-491 Business Catalog / Taxonomy (jspeidel via yhemanth) http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/repository/src/main/java/org/apache/atlas/RepositoryMetadataModule.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/RepositoryMetadataModule.java b/repository/src/main/java/org/apache/atlas/RepositoryMetadataModule.java index 68b707f..15201dd 100755 --- a/repository/src/main/java/org/apache/atlas/RepositoryMetadataModule.java +++ b/repository/src/main/java/org/apache/atlas/RepositoryMetadataModule.java @@ -111,7 +111,11 @@ public class RepositoryMetadataModule extends com.google.inject.AbstractModule { private static final String DELETE_HANDLER_IMPLEMENTATION_PROPERTY = "atlas.DeleteHandler.impl"; private Class<? extends DeleteHandler> getDeleteHandler() { - return ApplicationProperties.getClass(DELETE_HANDLER_IMPLEMENTATION_PROPERTY, - SoftDeleteHandler.class.getName()); + try { + return ApplicationProperties.getClass(DELETE_HANDLER_IMPLEMENTATION_PROPERTY, + SoftDeleteHandler.class.getName(), DeleteHandler.class); + } catch (AtlasException e) { + throw new RuntimeException(e); + } } } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypeSystem.java ---------------------------------------------------------------------- diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypeSystem.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypeSystem.java index 7763ebc..14f1968 100755 --- a/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypeSystem.java +++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/TypeSystem.java @@ -18,19 +18,6 @@ package org.apache.atlas.typesystem.types; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Multimap; -import org.apache.atlas.AtlasException; -import org.apache.atlas.classification.InterfaceAudience; -import org.apache.atlas.typesystem.TypesDef; -import org.apache.atlas.typesystem.exception.TypeExistsException; -import org.apache.atlas.typesystem.exception.TypeNotFoundException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Singleton; import java.lang.reflect.Constructor; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -41,10 +28,27 @@ import java.util.Set; import java.util.TimeZone; import java.util.concurrent.ConcurrentHashMap; +import javax.inject.Singleton; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasException; +import org.apache.atlas.classification.InterfaceAudience; +import org.apache.atlas.typesystem.TypesDef; +import org.apache.atlas.typesystem.exception.TypeExistsException; +import org.apache.atlas.typesystem.exception.TypeNotFoundException; +import org.apache.atlas.typesystem.types.cache.DefaultTypeCacheProvider; +import org.apache.atlas.typesystem.types.cache.ITypeCacheProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + @Singleton @InterfaceAudience.Private public class TypeSystem { private static final Logger LOG = LoggerFactory.getLogger(TypeSystem.class); + private static final String CACHE_PROVIDER_CLASS_PROPERTY = "atlas.typesystem.cache.provider"; private static final TypeSystem INSTANCE = new TypeSystem(); private static ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>() { @@ -56,15 +60,9 @@ public class TypeSystem { } }; - private Map<String, IDataType> types; + private ITypeCacheProvider typeCache; private IdType idType; - - /** - * An in-memory copy of type categories vs types for convenience. - */ - private Multimap<DataTypes.TypeCategory, String> typeCategoriesToTypeNamesMap; - - private ImmutableList<String> coreTypes; + private Map<String, IDataType> coreTypes; public TypeSystem() { initialize(); @@ -78,69 +76,111 @@ public class TypeSystem { * This is only used for testing purposes. Not intended for public use. */ @InterfaceAudience.Private - public void reset() { + public TypeSystem reset() { + + typeCache.clear(); // clear all entries in cache initialize(); + + return this; } private void initialize() { - types = new ConcurrentHashMap<>(); - typeCategoriesToTypeNamesMap = ArrayListMultimap.create(DataTypes.TypeCategory.values().length, 10); + + initCacheProvider(); + coreTypes = new ConcurrentHashMap<>(); registerPrimitiveTypes(); registerCoreTypes(); - coreTypes = ImmutableList.copyOf(types.keySet()); + } + + /** + * Ideally a cache provider should have been injected in the TypeSystemProvider, + * but a singleton of TypeSystem is constructed privately within the class so that + * clients of TypeSystem would never instantiate a TypeSystem object directly in + * their code. As soon as a client makes a call to TypeSystem.getInstance(), they + * should have the singleton ready for consumption. To enable such an access pattern, + * it kind of becomes imperative to initialize the cache provider within the + * TypeSystem constructor (bypassing the GUICE way of injecting a cache provider) + */ + private void initCacheProvider() { + + // read the pluggable cache provider from Atlas configuration + final String defaultCacheProvider = DefaultTypeCacheProvider.class.getName(); + Class cacheProviderClass; + try { + cacheProviderClass = ApplicationProperties.getClass(CACHE_PROVIDER_CLASS_PROPERTY, + defaultCacheProvider, ITypeCacheProvider.class); + } catch (AtlasException e) { + throw new RuntimeException("Error getting type cache provider implementation class", e); + } + + try { + typeCache = (ITypeCacheProvider)cacheProviderClass.newInstance(); + } + catch (Exception e) { + throw new RuntimeException("Error creating instance of type cache provider implementation class " + cacheProviderClass.getName(), e); + } } public ImmutableList<String> getCoreTypes() { - return coreTypes; + return ImmutableList.copyOf(coreTypes.keySet()); } - public ImmutableList<String> getTypeNames() { - List<String> typeNames = new ArrayList<>(types.keySet()); - typeNames.removeAll(getCoreTypes()); + public ImmutableList<String> getTypeNames() throws AtlasException { + List<String> typeNames = new ArrayList<>(typeCache.getAllTypeNames()); return ImmutableList.copyOf(typeNames); } - public ImmutableList<String> getTypeNamesByCategory(DataTypes.TypeCategory typeCategory) { - return ImmutableList.copyOf(typeCategoriesToTypeNamesMap.get(typeCategory)); + public ImmutableList<String> getTypeNamesByCategory(DataTypes.TypeCategory typeCategory) throws AtlasException { + return ImmutableList.copyOf(typeCache.getTypeNames(typeCategory)); } private void registerPrimitiveTypes() { - types.put(DataTypes.BOOLEAN_TYPE.getName(), DataTypes.BOOLEAN_TYPE); - types.put(DataTypes.BYTE_TYPE.getName(), DataTypes.BYTE_TYPE); - types.put(DataTypes.SHORT_TYPE.getName(), DataTypes.SHORT_TYPE); - types.put(DataTypes.INT_TYPE.getName(), DataTypes.INT_TYPE); - types.put(DataTypes.LONG_TYPE.getName(), DataTypes.LONG_TYPE); - types.put(DataTypes.FLOAT_TYPE.getName(), DataTypes.FLOAT_TYPE); - types.put(DataTypes.DOUBLE_TYPE.getName(), DataTypes.DOUBLE_TYPE); - types.put(DataTypes.BIGINTEGER_TYPE.getName(), DataTypes.BIGINTEGER_TYPE); - types.put(DataTypes.BIGDECIMAL_TYPE.getName(), DataTypes.BIGDECIMAL_TYPE); - types.put(DataTypes.DATE_TYPE.getName(), DataTypes.DATE_TYPE); - types.put(DataTypes.STRING_TYPE.getName(), DataTypes.STRING_TYPE); - typeCategoriesToTypeNamesMap.putAll(DataTypes.TypeCategory.PRIMITIVE, types.keySet()); + coreTypes.put(DataTypes.BOOLEAN_TYPE.getName(), DataTypes.BOOLEAN_TYPE); + coreTypes.put(DataTypes.BYTE_TYPE.getName(), DataTypes.BYTE_TYPE); + coreTypes.put(DataTypes.SHORT_TYPE.getName(), DataTypes.SHORT_TYPE); + coreTypes.put(DataTypes.INT_TYPE.getName(), DataTypes.INT_TYPE); + coreTypes.put(DataTypes.LONG_TYPE.getName(), DataTypes.LONG_TYPE); + coreTypes.put(DataTypes.FLOAT_TYPE.getName(), DataTypes.FLOAT_TYPE); + coreTypes.put(DataTypes.DOUBLE_TYPE.getName(), DataTypes.DOUBLE_TYPE); + coreTypes.put(DataTypes.BIGINTEGER_TYPE.getName(), DataTypes.BIGINTEGER_TYPE); + coreTypes.put(DataTypes.BIGDECIMAL_TYPE.getName(), DataTypes.BIGDECIMAL_TYPE); + coreTypes.put(DataTypes.DATE_TYPE.getName(), DataTypes.DATE_TYPE); + coreTypes.put(DataTypes.STRING_TYPE.getName(), DataTypes.STRING_TYPE); } - /* * The only core OOB type we will define is the Struct to represent the Identity of an Instance. */ private void registerCoreTypes() { + idType = new IdType(); + coreTypes.put(idType.getStructType().getName(), idType.getStructType()); } public IdType getIdType() { return idType; } - public boolean isRegistered(String typeName) { - return types.containsKey(typeName); + public boolean isRegistered(String typeName) throws AtlasException { + return isCoreType(typeName) || typeCache.has(typeName); + } + + protected boolean isCoreType(String typeName) { + + return coreTypes.containsKey(typeName); } public <T> T getDataType(Class<T> cls, String name) throws AtlasException { - if (types.containsKey(name)) { + + if (isCoreType(name)) { + return cls.cast(coreTypes.get(name)); + } + + if (typeCache.has(name)) { try { - return cls.cast(types.get(name)); + return cls.cast(typeCache.get(name)); } catch (ClassCastException cce) { throw new AtlasException(cce); } @@ -285,13 +325,12 @@ public class TypeSystem { public EnumType defineEnumType(EnumTypeDefinition eDef) throws AtlasException { assert eDef.name != null; - if (types.containsKey(eDef.name)) { + if (isRegistered(eDef.name)) { throw new AtlasException(String.format("Redefinition of type %s not supported", eDef.name)); } EnumType eT = new EnumType(this, eDef.name, eDef.description, eDef.enumValues); - types.put(eDef.name, eT); - typeCategoriesToTypeNamesMap.put(DataTypes.TypeCategory.ENUM, eDef.name); + typeCache.put(eT); return eT; } @@ -329,17 +368,14 @@ public class TypeSystem { * * This step should be called only after the types have been committed to the backend stores successfully. * @param typesAdded newly added types. + * @throws AtlasException */ - public void commitTypes(Map<String, IDataType> typesAdded) { + public void commitTypes(Map<String, IDataType> typesAdded) throws AtlasException { for (Map.Entry<String, IDataType> typeEntry : typesAdded.entrySet()) { String typeName = typeEntry.getKey(); IDataType type = typeEntry.getValue(); //Add/replace the new type in the typesystem - types.put(typeName, type); - // ArrayListMultiMap allows duplicates - we want to avoid this during re-activation. - if (!typeCategoriesToTypeNamesMap.containsEntry(type.getTypeCategory(), typeName)) { - typeCategoriesToTypeNamesMap.put(type.getTypeCategory(), typeName); - } + typeCache.put(type); } } @@ -372,10 +408,12 @@ public class TypeSystem { } private IDataType dataType(String name) throws AtlasException { + if (transientTypes.containsKey(name)) { return transientTypes.get(name); } - return TypeSystem.this.types.get(name); + + return TypeSystem.this.getDataType(IDataType.class, name); } /* @@ -386,7 +424,7 @@ public class TypeSystem { private void validateAndSetupShallowTypes(boolean update) throws AtlasException { for (EnumTypeDefinition eDef : enumDefs) { assert eDef.name != null; - if (!update && (transientTypes.containsKey(eDef.name) || types.containsKey(eDef.name))) { + if (!update && (transientTypes.containsKey(eDef.name) || isRegistered(eDef.name))) { throw new AtlasException(String.format("Redefinition of type %s not supported", eDef.name)); } @@ -396,7 +434,7 @@ public class TypeSystem { for (StructTypeDefinition sDef : structDefs) { assert sDef.typeName != null; - if (!update && (transientTypes.containsKey(sDef.typeName) || types.containsKey(sDef.typeName))) { + if (!update && (transientTypes.containsKey(sDef.typeName) || isRegistered(sDef.typeName))) { throw new TypeExistsException(String.format("Cannot redefine type %s", sDef.typeName)); } StructType sT = new StructType(this, sDef.typeName, sDef.typeDescription, sDef.attributeDefinitions.length); @@ -407,7 +445,7 @@ public class TypeSystem { for (HierarchicalTypeDefinition<TraitType> traitDef : traitDefs) { assert traitDef.typeName != null; if (!update && - (transientTypes.containsKey(traitDef.typeName) || types.containsKey(traitDef.typeName))) { + (transientTypes.containsKey(traitDef.typeName) || isRegistered(traitDef.typeName))) { throw new TypeExistsException(String.format("Cannot redefine type %s", traitDef.typeName)); } TraitType tT = new TraitType(this, traitDef.typeName, traitDef.typeDescription, traitDef.superTypes, @@ -419,7 +457,7 @@ public class TypeSystem { for (HierarchicalTypeDefinition<ClassType> classDef : classDefs) { assert classDef.typeName != null; if (!update && - (transientTypes.containsKey(classDef.typeName) || types.containsKey(classDef.typeName))) { + (transientTypes.containsKey(classDef.typeName) || isRegistered(classDef.typeName))) { throw new TypeExistsException(String.format("Cannot redefine type %s", classDef.typeName)); } @@ -582,11 +620,11 @@ public class TypeSystem { * Step 5: * - Validate that the update can be done */ - private void validateUpdateIsPossible() throws TypeUpdateException { + private void validateUpdateIsPossible() throws TypeUpdateException, AtlasException { //If the type is modified, validate that update can be done for (IDataType newType : transientTypes.values()) { - if (TypeSystem.this.types.containsKey(newType.getName())) { - IDataType oldType = TypeSystem.this.types.get(newType.getName()); + if (TypeSystem.this.isRegistered(newType.getName())) { + IDataType oldType = TypeSystem.this.typeCache.get(newType.getName()); oldType.validateUpdate(newType); } } @@ -600,7 +638,7 @@ public class TypeSystem { } @Override - public ImmutableList<String> getTypeNames() { + public ImmutableList<String> getTypeNames() throws AtlasException { Set<String> typeNames = transientTypes.keySet(); typeNames.addAll(TypeSystem.this.getTypeNames()); return ImmutableList.copyOf(typeNames); @@ -644,17 +682,17 @@ public class TypeSystem { @Override public StructType defineStructType(String name, boolean errorIfExists, AttributeDefinition... attrDefs) throws AtlasException { - throw new AtlasException("Internal Error: define type called on TrasientTypeSystem"); + throw new AtlasException("Internal Error: define type called on TransientTypeSystem"); } @Override public TraitType defineTraitType(HierarchicalTypeDefinition traitDef) throws AtlasException { - throw new AtlasException("Internal Error: define type called on TrasientTypeSystem"); + throw new AtlasException("Internal Error: define type called on TransientTypeSystem"); } @Override public ClassType defineClassType(HierarchicalTypeDefinition<ClassType> classDef) throws AtlasException { - throw new AtlasException("Internal Error: define type called on TrasientTypeSystem"); + throw new AtlasException("Internal Error: define type called on TransientTypeSystem"); } @Override @@ -662,7 +700,7 @@ public class TypeSystem { ImmutableList<StructTypeDefinition> structDefs, ImmutableList<HierarchicalTypeDefinition<TraitType>> traitDefs, ImmutableList<HierarchicalTypeDefinition<ClassType>> classDefs) throws AtlasException { - throw new AtlasException("Internal Error: define type called on TrasientTypeSystem"); + throw new AtlasException("Internal Error: define type called on TransientTypeSystem"); } @Override @@ -686,13 +724,22 @@ public class TypeSystem { } @Override - public void commitTypes(Map<String, IDataType> typesAdded) { + public void commitTypes(Map<String, IDataType> typesAdded) throws AtlasException { TypeSystem.this.commitTypes(typesAdded); } public Map<String, IDataType> getTypesAdded() { return new HashMap<>(transientTypes); } + + /** + * The core types do not change and they are registered + * once in the main type system. + */ + @Override + public ImmutableList<String> getCoreTypes() { + return TypeSystem.this.getCoreTypes(); + } } public class IdType { @@ -700,6 +747,8 @@ public class TypeSystem { private static final String TYPENAME_ATTRNAME = "typeName"; private static final String TYP_NAME = "__IdType"; + private StructType type; + private IdType() { AttributeDefinition idAttr = new AttributeDefinition(ID_ATTRNAME, DataTypes.STRING_TYPE.getName(), Multiplicity.REQUIRED, false, @@ -712,16 +761,14 @@ public class TypeSystem { infos[0] = new AttributeInfo(TypeSystem.this, idAttr, null); infos[1] = new AttributeInfo(TypeSystem.this, typNmAttr, null); - StructType type = new StructType(TypeSystem.this, TYP_NAME, null, infos); - TypeSystem.this.types.put(TYP_NAME, type); - + type = new StructType(TypeSystem.this, TYP_NAME, null, infos); } catch (AtlasException me) { throw new RuntimeException(me); } } - public StructType getStructType() throws AtlasException { - return getDataType(StructType.class, TYP_NAME); + public StructType getStructType() { + return type; } public String getName() { @@ -738,4 +785,5 @@ public class TypeSystem { } public static final String ID_STRUCT_ID_ATTRNAME = IdType.ID_ATTRNAME; + public static final String ID_STRUCT_TYP_NAME = IdType.TYP_NAME; } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/typesystem/src/main/java/org/apache/atlas/typesystem/types/cache/DefaultTypeCacheProvider.java ---------------------------------------------------------------------- diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/cache/DefaultTypeCacheProvider.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/cache/DefaultTypeCacheProvider.java new file mode 100644 index 0000000..700bda6 --- /dev/null +++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/cache/DefaultTypeCacheProvider.java @@ -0,0 +1,229 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.atlas.typesystem.types.cache; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.atlas.AtlasException; +import org.apache.atlas.typesystem.types.ClassType; +import org.apache.atlas.typesystem.types.DataTypes.TypeCategory; +import org.apache.atlas.typesystem.types.EnumType; +import org.apache.atlas.typesystem.types.IDataType; +import org.apache.atlas.typesystem.types.StructType; +import org.apache.atlas.typesystem.types.TraitType; + +import com.google.inject.Singleton; + +/** + * Caches the types in-memory within the same process space. + */ +@Singleton +@SuppressWarnings("rawtypes") +public class DefaultTypeCacheProvider implements ITypeCacheProvider { + + private Map<String, IDataType> types_ = new ConcurrentHashMap<>(); + + /* + * (non-Javadoc) + * @see + * org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#has(java.lang + * .String) + */ + @Override + public boolean has(String typeName) throws AtlasException { + + return types_.containsKey(typeName); + } + + /* (non-Javadoc) + * @see org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#has(org. + * apache.atlas.typesystem.types.DataTypes.TypeCategory, java.lang.String) + */ + @Override + public boolean has(TypeCategory typeCategory, String typeName) + throws AtlasException { + + assertValidTypeCategory(typeCategory); + return has(typeName); + } + + private void assertValidTypeCategory(TypeCategory typeCategory) throws + AtlasException { + + // there might no need of 'typeCategory' in this implementation for + // certain API, but for a distributed cache, it might help for the + // implementers to partition the types per their category + // while persisting so that look can be efficient + + if (typeCategory == null) { + throw new AtlasException("Category of the types to be filtered is null."); + } + + boolean validTypeCategory = typeCategory.equals(TypeCategory.CLASS) || + typeCategory.equals(TypeCategory.TRAIT) || + typeCategory.equals(TypeCategory.ENUM) || + typeCategory.equals(TypeCategory.STRUCT); + + if (!validTypeCategory) { + throw new AtlasException("Category of the types should be one of CLASS " + + "| TRAIT | ENUM | STRUCT."); + } + } + + /* + * (non-Javadoc) + * @see + * org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#get(java.lang + * .String) + */ + @Override + public IDataType get(String typeName) throws AtlasException { + + return types_.get(typeName); + } + + /* (non-Javadoc) + * @see org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#get(org.apache. + * atlas.typesystem.types.DataTypes.TypeCategory, java.lang.String) + */ + @Override + public IDataType get(TypeCategory typeCategory, String typeName) throws AtlasException { + + assertValidTypeCategory(typeCategory); + return get(typeName); + } + + /* + * (non-Javadoc) + * @see + * org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#getNames(org + * .apache.atlas.typesystem.types.DataTypes.TypeCategory) + */ + @Override + public Collection<String> getTypeNames(TypeCategory typeCategory) throws AtlasException { + + assertValidTypeCategory(typeCategory); + + List<String> typeNames = new ArrayList<>(); + for (Entry<String, IDataType> typeEntry : types_.entrySet()) { + String name = typeEntry.getKey(); + IDataType type = typeEntry.getValue(); + + if (type.getTypeCategory().equals(typeCategory)) { + typeNames.add(name); + } + } + return typeNames; + } + + /* + * (non-Javadoc) + * @see + * org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#getAllNames() + */ + @Override + public Collection<String> getAllTypeNames() throws AtlasException { + + return types_.keySet(); + } + + /* + * (non-Javadoc) + * @see + * org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#put(org.apache + * .atlas.typesystem.types.IDataType) + */ + @Override + public void put(IDataType type) throws AtlasException { + + assertValidType(type); + types_.put(type.getName(), type); + } + + private void assertValidType(IDataType type) throws + AtlasException { + + if (type == null) { + throw new AtlasException("type is null."); + } + + boolean validTypeCategory = (type instanceof ClassType) || + (type instanceof TraitType) || + (type instanceof EnumType) || + (type instanceof StructType); + + if (!validTypeCategory) { + throw new AtlasException("Category of the types should be one of ClassType | " + + "TraitType | EnumType | StructType."); + } + } + + /* + * (non-Javadoc) + * @see + * org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#putAll(java + * .util.Collection) + */ + @Override + public void putAll(Collection<IDataType> types) throws AtlasException { + + for (IDataType type : types) { + assertValidType(type); + types_.put(type.getName(), type); + } + } + + /* + * (non-Javadoc) + * @see + * org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#remove(java + * .lang.String) + */ + @Override + public void remove(String typeName) throws AtlasException { + + types_.remove(typeName); + } + + /* (non-Javadoc) + * @see org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#remove(org. + * apache.atlas.typesystem.types.DataTypes.TypeCategory, java.lang.String) + */ + @Override + public void remove(TypeCategory typeCategory, String typeName) + throws AtlasException { + + assertValidTypeCategory(typeCategory); + remove(typeName); + } + + /* + * (non-Javadoc) + * @see org.apache.atlas.typesystem.types.cache.ITypeCacheProvider#clear() + */ + @Override + public void clear() { + + types_.clear(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/typesystem/src/main/java/org/apache/atlas/typesystem/types/cache/ITypeCacheProvider.java ---------------------------------------------------------------------- diff --git a/typesystem/src/main/java/org/apache/atlas/typesystem/types/cache/ITypeCacheProvider.java b/typesystem/src/main/java/org/apache/atlas/typesystem/types/cache/ITypeCacheProvider.java new file mode 100644 index 0000000..55e9ddc --- /dev/null +++ b/typesystem/src/main/java/org/apache/atlas/typesystem/types/cache/ITypeCacheProvider.java @@ -0,0 +1,137 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.atlas.typesystem.types.cache; + +import java.util.Collection; + +import org.apache.atlas.AtlasException; +import org.apache.atlas.typesystem.types.DataTypes; +import org.apache.atlas.typesystem.types.IDataType; + +/** + * The types are cached to allow faster lookup when type info is needed during + * creation/updation of entities, DSL query translation/execution. + * Implementations of this can chose to plugin a distributed cache provider + * or an in-memory cache synched across nodes in an Altas cluster. <br> + * <br> + * Type entries in the cache can be one of ... <br> + * {@link org.apache.atlas.typesystem.types.ClassType} <br> + * {@link org.apache.atlas.typesystem.types.TraitType} <br> + * {@link org.apache.atlas.typesystem.types.StructType} <br> + * {@link org.apache.atlas.typesystem.types.EnumType} + */ +@SuppressWarnings("rawtypes") +public interface ITypeCacheProvider { + + /** + * @param typeName + * @return true if the type exists in cache, false otherwise. + * @throws AtlasException + */ + boolean has(String typeName) throws AtlasException; + + /** + * @param typeCategory Non-null category of type. The category can be one of + * TypeCategory.CLASS | TypeCategory.TRAIT | TypeCategory.STRUCT | TypeCategory.ENUM. + * @param typeName + * @return true if the type of given category exists in cache, false otherwise. + * @throws AtlasException + */ + boolean has(DataTypes.TypeCategory typeCategory, String typeName) throws AtlasException; + + /** + * @param name The name of the type. + * @return returns non-null type if cached, otherwise null + * @throws AtlasException + */ + public IDataType get(String typeName) throws AtlasException; + + /** + * @param typeCategory Non-null category of type. The category can be one of + * TypeCategory.CLASS | TypeCategory.TRAIT | TypeCategory.STRUCT | TypeCategory.ENUM. + * @param typeName + * @return returns non-null type (of the specified category) if cached, otherwise null + * @throws AtlasException + */ + public IDataType get(DataTypes.TypeCategory typeCategory, String typeName) throws AtlasException; + + /** + * @param typeCategory The category of types to filter the returned types. Cannot be null. + * The category can be one of TypeCategory.CLASS | TypeCategory.TRAIT | + * TypeCategory.STRUCT | TypeCategory.ENUM. + * @return + * @throws AtlasException + */ + Collection<String> getTypeNames(DataTypes.TypeCategory typeCategory) throws AtlasException; + + /** + * This is a convenience API to get the names of all types. + * + * @see ITypeCacheProvider#getTypeNames(org.apache.atlas.typesystem.types.DataTypes.TypeCategory) + * @return + * @throws AtlasException + */ + Collection<String> getAllTypeNames() throws AtlasException; + + /** + * @param type The type to be added to the cache. The type should not be + * null, otherwise throws NullPointerException. <br> + * Type entries in the cache can be one of ... <br> + * {@link org.apache.atlas.typesystem.types.ClassType} <br> + * {@link org.apache.atlas.typesystem.types.TraitType} <br> + * {@link org.apache.atlas.typesystem.types.StructType} <br> + * {@link org.apache.atlas.typesystem.types.EnumType} + * @throws AtlasException + */ + void put(IDataType type) throws AtlasException; + + /** + * @param types The types to be added to the cache. The type should not be + * null, otherwise throws NullPointerException. <br> + * Type entries in the cache can be one of ... <br> + * {@link org.apache.atlas.typesystem.types.ClassType} <br> + * {@link org.apache.atlas.typesystem.types.TraitType} <br> + * {@link org.apache.atlas.typesystem.types.StructType} <br> + * {@link org.apache.atlas.typesystem.types.EnumType} + * @throws AtlasException + */ + void putAll(Collection<IDataType> types) throws AtlasException; + + /** + * @param typeName Name of the type to be removed from the cache. If type + * exists, it will be removed, otherwise does nothing. + * @throws AtlasException + */ + void remove(String typeName) throws AtlasException; + + /** + * @param typeCategory Non-null category of type. The category can be one of + * TypeCategory.CLASS | TypeCategory.TRAIT | TypeCategory.STRUCT | TypeCategory.ENUM. + * @param typeName Name of the type to be removed from the cache. If type + * exists, it will be removed, otherwise does nothing. + * @throws AtlasException + */ + void remove(DataTypes.TypeCategory typeCategory, String typeName) throws AtlasException; + + /** + * Clear the type cache + * + */ + void clear(); +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/typesystem/src/test/java/org/apache/atlas/ApplicationPropertiesTest.java ---------------------------------------------------------------------- diff --git a/typesystem/src/test/java/org/apache/atlas/ApplicationPropertiesTest.java b/typesystem/src/test/java/org/apache/atlas/ApplicationPropertiesTest.java index 27f1f7e..0679642 100644 --- a/typesystem/src/test/java/org/apache/atlas/ApplicationPropertiesTest.java +++ b/typesystem/src/test/java/org/apache/atlas/ApplicationPropertiesTest.java @@ -19,6 +19,7 @@ package org.apache.atlas; import org.apache.atlas.typesystem.types.TypeSystem; import org.apache.commons.configuration.Configuration; +import org.testng.Assert; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; @@ -58,11 +59,20 @@ public class ApplicationPropertiesTest { @Test public void testGetClass() throws Exception { //read from atlas-application.properties - Class cls = ApplicationProperties.getClass("atlas.TypeSystem.impl", ApplicationProperties.class.getName()); + Class cls = ApplicationProperties.getClass("atlas.TypeSystem.impl", ApplicationProperties.class.getName(), TypeSystem.class); assertEquals(cls.getName(), TypeSystem.class.getName()); //default value - cls = ApplicationProperties.getClass("atlas.TypeSystem2.impl", TypeSystem.class.getName()); + cls = ApplicationProperties.getClass("atlas.TypeSystem2.impl", TypeSystem.class.getName(), TypeSystem.class); assertEquals(cls.getName(), TypeSystem.class.getName()); + + //incompatible assignTo class, should throw AtlasException + try { + cls = ApplicationProperties.getClass("atlas.TypeSystem.impl", ApplicationProperties.class.getName(), ApplicationProperties.class); + Assert.fail(AtlasException.class.getSimpleName() + " was expected but none thrown."); + } + catch (AtlasException e) { + // good + } } } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/typesystem/src/test/java/org/apache/atlas/typesystem/types/TypeSystemTest.java ---------------------------------------------------------------------- diff --git a/typesystem/src/test/java/org/apache/atlas/typesystem/types/TypeSystemTest.java b/typesystem/src/test/java/org/apache/atlas/typesystem/types/TypeSystemTest.java index c0574ce..574e0f9 100755 --- a/typesystem/src/test/java/org/apache/atlas/typesystem/types/TypeSystemTest.java +++ b/typesystem/src/test/java/org/apache/atlas/typesystem/types/TypeSystemTest.java @@ -209,7 +209,7 @@ public class TypeSystemTest extends BaseTest { } @Test - public void testTypeNamesAreNotDuplicated() { + public void testTypeNamesAreNotDuplicated() throws Exception { TypeSystem typeSystem = getTypeSystem(); ImmutableList<String> traitNames = typeSystem.getTypeNamesByCategory(DataTypes.TypeCategory.TRAIT); int numTraits = traitNames.size(); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/5f248157/typesystem/src/test/java/org/apache/atlas/typesystem/types/cache/DefaultTypeCacheProviderTest.java ---------------------------------------------------------------------- diff --git a/typesystem/src/test/java/org/apache/atlas/typesystem/types/cache/DefaultTypeCacheProviderTest.java b/typesystem/src/test/java/org/apache/atlas/typesystem/types/cache/DefaultTypeCacheProviderTest.java new file mode 100644 index 0000000..c426d50 --- /dev/null +++ b/typesystem/src/test/java/org/apache/atlas/typesystem/types/cache/DefaultTypeCacheProviderTest.java @@ -0,0 +1,383 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.atlas.typesystem.types.cache; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.atlas.AtlasException; +import org.apache.atlas.AtlasException; +import org.apache.atlas.typesystem.types.ClassType; +import org.apache.atlas.typesystem.types.DataTypes; +import org.apache.atlas.typesystem.types.DataTypes.TypeCategory; +import org.apache.atlas.typesystem.types.EnumType; +import org.apache.atlas.typesystem.types.EnumValue; +import org.apache.atlas.typesystem.types.IDataType; +import org.apache.atlas.typesystem.types.StructType; +import org.apache.atlas.typesystem.types.TraitType; +import org.apache.atlas.typesystem.types.TypeSystem; +import org.apache.atlas.typesystem.types.utils.TypesUtil; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + +/** + * Tests functional behavior of {@link DefaultTypeCacheProvider} + */ +@SuppressWarnings("rawtypes") +public class DefaultTypeCacheProviderTest { + + private String CLASSTYPE_CUSTOMER = "Customer"; + private String STRUCTTYPE_ADDRESS = "Address"; + private String TRAITTYPE_PRIVILEGED = "Privileged"; + private String ENUMTYPE_SHIPPING = "Shipping"; + + private String UNKNOWN_TYPE = "UndefinedType"; + + private ClassType customerType; + private StructType addressType; + private TraitType privilegedTrait; + private EnumType shippingEnum; + + private DefaultTypeCacheProvider cacheProvider; + + @BeforeClass + public void onetimeSetup() throws Exception { + + // init TypeSystem + TypeSystem ts = TypeSystem.getInstance().reset(); + + // Customer ClassType + customerType = ts.defineClassType(TypesUtil + .createClassTypeDef(CLASSTYPE_CUSTOMER, ImmutableSet.<String>of(), + TypesUtil.createRequiredAttrDef("name", DataTypes.STRING_TYPE), + TypesUtil.createRequiredAttrDef("id", DataTypes.LONG_TYPE))); + + // Address StructType + addressType = ts.defineStructType(STRUCTTYPE_ADDRESS, true, + TypesUtil.createRequiredAttrDef("first line", DataTypes.STRING_TYPE), + TypesUtil.createOptionalAttrDef("second line", DataTypes.STRING_TYPE), + TypesUtil.createRequiredAttrDef("city", DataTypes.STRING_TYPE), + TypesUtil.createRequiredAttrDef("pincode", DataTypes.INT_TYPE)); + + // Privileged TraitType + privilegedTrait = ts.defineTraitType(TypesUtil + .createTraitTypeDef(TRAITTYPE_PRIVILEGED, ImmutableSet.<String>of(), + TypesUtil.createRequiredAttrDef("category", DataTypes.INT_TYPE))); + + // Shipping EnumType + shippingEnum = ts.defineEnumType(TypesUtil.createEnumTypeDef(ENUMTYPE_SHIPPING, + new EnumValue("Domestic", 1), new EnumValue("International", 2))); + } + + @BeforeMethod + public void eachTestSetup() throws Exception { + + cacheProvider = new DefaultTypeCacheProvider(); + + cacheProvider.put(customerType); + cacheProvider.put(addressType); + cacheProvider.put(privilegedTrait); + cacheProvider.put(shippingEnum); + } + + @Test + public void testCacheGetType() throws Exception { + + IDataType custType = cacheProvider.get(CLASSTYPE_CUSTOMER); + verifyType(custType, CLASSTYPE_CUSTOMER, ClassType.class); + + IDataType addrType = cacheProvider.get(STRUCTTYPE_ADDRESS); + verifyType(addrType, STRUCTTYPE_ADDRESS, StructType.class); + + IDataType privTrait = cacheProvider.get(TRAITTYPE_PRIVILEGED); + verifyType(privTrait, TRAITTYPE_PRIVILEGED, TraitType.class); + + IDataType shippingEnum = cacheProvider.get(ENUMTYPE_SHIPPING); + verifyType(shippingEnum, ENUMTYPE_SHIPPING, EnumType.class); + + assertNull(cacheProvider.get(UNKNOWN_TYPE)); + } + + @Test + public void testCacheGetTypeByCategory() throws Exception { + + IDataType custType = cacheProvider.get(TypeCategory.CLASS, CLASSTYPE_CUSTOMER); + verifyType(custType, CLASSTYPE_CUSTOMER, ClassType.class); + + IDataType addrType = cacheProvider.get(TypeCategory.STRUCT, STRUCTTYPE_ADDRESS); + verifyType(addrType, STRUCTTYPE_ADDRESS, StructType.class); + + IDataType privTrait = cacheProvider.get(TypeCategory.TRAIT, TRAITTYPE_PRIVILEGED); + verifyType(privTrait, TRAITTYPE_PRIVILEGED, TraitType.class); + + IDataType shippingEnum = cacheProvider.get(TypeCategory.ENUM, ENUMTYPE_SHIPPING); + verifyType(shippingEnum, ENUMTYPE_SHIPPING, EnumType.class); + + assertNull(cacheProvider.get(UNKNOWN_TYPE)); + } + + private void verifyType(IDataType actualType, String expectedName, Class<? extends IDataType> typeClass) { + + assertNotNull(actualType, "The " + expectedName + " type not in cache"); + assertTrue(typeClass.isInstance(actualType)); + assertEquals(actualType.getName(), expectedName, "The type name does not match"); + } + + @Test + public void testCacheHasType() throws Exception { + + assertTrue(cacheProvider.has(CLASSTYPE_CUSTOMER)); + assertTrue(cacheProvider.has(STRUCTTYPE_ADDRESS)); + assertTrue(cacheProvider.has(TRAITTYPE_PRIVILEGED)); + assertTrue(cacheProvider.has(ENUMTYPE_SHIPPING)); + + assertFalse(cacheProvider.has(UNKNOWN_TYPE)); + } + + @Test + public void testCacheHasTypeByCategory() throws Exception { + + assertTrue(cacheProvider.has(TypeCategory.CLASS, CLASSTYPE_CUSTOMER)); + assertTrue(cacheProvider.has(TypeCategory.STRUCT, STRUCTTYPE_ADDRESS)); + assertTrue(cacheProvider.has(TypeCategory.TRAIT, TRAITTYPE_PRIVILEGED)); + assertTrue(cacheProvider.has(TypeCategory.ENUM, ENUMTYPE_SHIPPING)); + + assertFalse(cacheProvider.has(UNKNOWN_TYPE)); + } + + @Test + public void testCacheGetAllTypeNames() throws Exception { + + List<String> allTypeNames = new ArrayList<String>(cacheProvider.getAllTypeNames()); + Collections.sort(allTypeNames); + + final int EXPECTED_TYPE_COUNT = 4; + assertEquals(allTypeNames.size(), EXPECTED_TYPE_COUNT, "Total number of types does not match."); + + assertEquals(STRUCTTYPE_ADDRESS, allTypeNames.get(0)); + assertEquals(CLASSTYPE_CUSTOMER, allTypeNames.get(1)); + assertEquals(TRAITTYPE_PRIVILEGED, allTypeNames.get(2)); + assertEquals(ENUMTYPE_SHIPPING, allTypeNames.get(3)); + } + + @Test + public void testCacheGetTypeNamesByCategory() throws Exception { + + List<String> classTypes = new ArrayList<String>(cacheProvider.getTypeNames(TypeCategory.CLASS)); + final int EXPECTED_CLASSTYPE_COUNT = 1; + assertEquals(classTypes.size(), EXPECTED_CLASSTYPE_COUNT); + assertEquals(CLASSTYPE_CUSTOMER, classTypes.get(0)); + + List<String> structTypes = new ArrayList<String>(cacheProvider.getTypeNames(TypeCategory.STRUCT)); + final int EXPECTED_STRUCTTYPE_COUNT = 1; + assertEquals(structTypes.size(), EXPECTED_STRUCTTYPE_COUNT); + assertEquals(STRUCTTYPE_ADDRESS, structTypes.get(0)); + + List<String> traitTypes = new ArrayList<String>(cacheProvider.getTypeNames(TypeCategory.TRAIT)); + final int EXPECTED_TRAITTYPE_COUNT = 1; + assertEquals(traitTypes.size(), EXPECTED_TRAITTYPE_COUNT); + assertEquals(TRAITTYPE_PRIVILEGED, traitTypes.get(0)); + + List<String> enumTypes = new ArrayList<String>(cacheProvider.getTypeNames(TypeCategory.ENUM)); + final int EXPECTED_ENUMTYPE_COUNT = 1; + assertEquals(enumTypes.size(), EXPECTED_ENUMTYPE_COUNT); + assertEquals(ENUMTYPE_SHIPPING, enumTypes.get(0)); + } + + @Test + public void testCacheBulkInsert() throws Exception { + + List<IDataType> allTypes = new ArrayList<>(); + allTypes.add(customerType); + allTypes.add(addressType); + allTypes.add(privilegedTrait); + allTypes.add(shippingEnum); + + // create a new cache provider instead of using the one setup for every method call + cacheProvider = new DefaultTypeCacheProvider(); + cacheProvider.putAll(allTypes); + + IDataType custType = cacheProvider.get(CLASSTYPE_CUSTOMER); + verifyType(custType, CLASSTYPE_CUSTOMER, ClassType.class); + + IDataType addrType = cacheProvider.get(STRUCTTYPE_ADDRESS); + verifyType(addrType, STRUCTTYPE_ADDRESS, StructType.class); + + IDataType privTrait = cacheProvider.get(TRAITTYPE_PRIVILEGED); + verifyType(privTrait, TRAITTYPE_PRIVILEGED, TraitType.class); + + IDataType shippingEnum = cacheProvider.get(ENUMTYPE_SHIPPING); + verifyType(shippingEnum, ENUMTYPE_SHIPPING, EnumType.class); + } + + @Test + public void testCacheRemove() throws Exception { + + cacheProvider.remove(CLASSTYPE_CUSTOMER); + assertNull(cacheProvider.get(CLASSTYPE_CUSTOMER)); + assertFalse(cacheProvider.has(CLASSTYPE_CUSTOMER)); + assertTrue(cacheProvider.getTypeNames(TypeCategory.CLASS).isEmpty()); + + final int EXPECTED_TYPE_COUNT = 3; + assertEquals(cacheProvider.getAllTypeNames().size(), EXPECTED_TYPE_COUNT); + } + + @Test + public void testCacheRemoveByCategory() throws Exception { + + cacheProvider.remove(TypeCategory.CLASS, CLASSTYPE_CUSTOMER); + assertNull(cacheProvider.get(CLASSTYPE_CUSTOMER)); + assertFalse(cacheProvider.has(CLASSTYPE_CUSTOMER)); + assertTrue(cacheProvider.getTypeNames(TypeCategory.CLASS).isEmpty()); + + final int EXPECTED_TYPE_COUNT = 3; + assertEquals(cacheProvider.getAllTypeNames().size(), EXPECTED_TYPE_COUNT); + } + + @Test + public void testCacheClear() throws Exception { + + cacheProvider.clear(); + + assertNull(cacheProvider.get(CLASSTYPE_CUSTOMER)); + assertFalse(cacheProvider.has(CLASSTYPE_CUSTOMER)); + + assertNull(cacheProvider.get(STRUCTTYPE_ADDRESS)); + assertFalse(cacheProvider.has(STRUCTTYPE_ADDRESS)); + + assertNull(cacheProvider.get(TRAITTYPE_PRIVILEGED)); + assertFalse(cacheProvider.has(TRAITTYPE_PRIVILEGED)); + + assertNull(cacheProvider.get(ENUMTYPE_SHIPPING)); + assertFalse(cacheProvider.has(ENUMTYPE_SHIPPING)); + + assertTrue(cacheProvider.getTypeNames(TypeCategory.CLASS).isEmpty()); + assertTrue(cacheProvider.getTypeNames(TypeCategory.STRUCT).isEmpty()); + assertTrue(cacheProvider.getTypeNames(TypeCategory.TRAIT).isEmpty()); + assertTrue(cacheProvider.getTypeNames(TypeCategory.ENUM).isEmpty()); + + assertTrue(cacheProvider.getAllTypeNames().isEmpty()); + } + + @Test(expectedExceptions = AtlasException.class) + public void testPutTypeWithNullType() throws Exception { + + cacheProvider.put(null); + fail("Null type should be not allowed in 'put'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testPutTypeWithInvalidType() throws Exception { + + cacheProvider.put(DataTypes.BOOLEAN_TYPE); + fail("type should only be an instance of ClassType | EnumType | StructType | TraitType in 'put'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testGetTypeWithNullCategory() throws Exception { + + cacheProvider.get(null, CLASSTYPE_CUSTOMER); + fail("Null TypeCategory should be not allowed in 'get'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testGetTypeWithInvalidCategory() throws Exception { + + cacheProvider.get(TypeCategory.PRIMITIVE, DataTypes.BOOLEAN_TYPE.getName()); + fail("TypeCategory should only be one of TypeCategory.CLASS | ENUM | STRUCT | TRAIT in 'get'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testCacheHasTypeWithNullCategory() throws Exception { + + cacheProvider.has(null, CLASSTYPE_CUSTOMER); + fail("Null TypeCategory should be not allowed in 'has'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testCacheHasTypeWithInvalidCategory() throws Exception { + + cacheProvider.has(TypeCategory.PRIMITIVE, DataTypes.BOOLEAN_TYPE.getName()); + fail("TypeCategory should only be one of TypeCategory.CLASS | ENUM | STRUCT | TRAIT in 'has'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testCacheGetTypeNamesByNullCategory() throws Exception { + + cacheProvider.getTypeNames(null); + fail("Null TypeCategory should be not allowed in 'getNames'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testCacheGetTypeNamesByInvalidCategory() throws Exception { + + cacheProvider.getTypeNames(TypeCategory.PRIMITIVE); + fail("TypeCategory should only be one of TypeCategory.CLASS | ENUM | STRUCT | TRAIT in 'getNames'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testCacheBulkInsertWithNullType() throws Exception { + + List<IDataType> allTypes = new ArrayList<>(); + allTypes.add(null); + + // create a new cache provider instead of using the one setup for every method call + cacheProvider = new DefaultTypeCacheProvider(); + cacheProvider.putAll(allTypes); + + fail("Null type should be not allowed in 'putAll'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testCacheBulkInsertWithInvalidType() throws Exception { + + List<IDataType> allTypes = new ArrayList<>(); + allTypes.add(DataTypes.BOOLEAN_TYPE); + + // create a new cache provider instead of using the one setup for every method call + cacheProvider = new DefaultTypeCacheProvider(); + cacheProvider.putAll(allTypes); + + fail("type should only one of ClassType | EnumType | StructType | TraitType in 'putAll'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testCacheRemoveByNullCategory() throws Exception { + + cacheProvider.remove(null, CLASSTYPE_CUSTOMER); + fail("Null type should be not allowed in 'remove'"); + } + + @Test(expectedExceptions = AtlasException.class) + public void testCacheRemoveByInvalidCategory() throws Exception { + + cacheProvider.remove(TypeCategory.PRIMITIVE, DataTypes.BOOLEAN_TYPE.getName()); + fail("TypeCategory should only be one of TypeCategory.CLASS | ENUM | STRUCT | TRAIT in 'remove'"); + } +}
