org.firebrandocm.dao.ClassMeta.java Source code

Java tutorial

Introduction

Here is the source code for org.firebrandocm.dao.ClassMeta.java

Source

    package smn.learn.ocm;
    /*
     * Copyright (C) 2012 47 Degrees, LLC
     * http://47deg.com
     * hello@47deg.com
     *
     * Licensed 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.firebrandocm.dao;

    import javassist.util.proxy.MethodFilter;
    import javassist.util.proxy.MethodHandler;
    import javassist.util.proxy.ProxyFactory;
    import javassist.util.proxy.ProxyObject;
    import me.prettyprint.cassandra.serializers.StringSerializer;
    import org.apache.cassandra.db.marshal.CounterColumnType;
    import org.apache.cassandra.thrift.CfDef;
    import org.apache.cassandra.thrift.ColumnDef;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.firebrandocm.dao.annotations.*;
    import org.firebrandocm.dao.events.Event;
    import org.firebrandocm.dao.utils.ObjectUtils;

    import java.beans.IntrospectionException;
    import java.beans.PropertyDescriptor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.*;

    /**
     * Holds metadata information about a class, initialized at startup time and reused
     */
    public class ClassMeta<T> {
        /* Fields */

        private static final String CLASS_PROPERTY = "class";

        private static Map<String, String> namedQueries = new HashMap<String, String>();

        private Log log = LogFactory.getLog(getClass());

        /**
         * the keyspace this entity belongs to
         */
        private String keySpace;

        /**
         * a column family for this entity
         */
        private String columnFamily;

        /**
         * the entity's class
         */
        private Class<T> target;

        /**
         * the set of all class properties
         */
        private Set<String> mutationProperties = new HashSet<String>();

        /**
         * the set of all readable properties
         */
        private Set<String> selectionProperties = new HashSet<String>();

        /**
         * the set of all persistent properties that declared themselves as being part of secondary indexes
         */
        private Set<String> indexedProperties = new HashSet<String>();

        /**
         * the property that holds the key in this class
         */
        private String keyProperty;

        /**
         * a map from columns to types
         */
        private Map<String, Class<?>> propertiesTypesMap = new HashMap<String, Class<?>>();

        /**
         * set of properties that map to embedded entities
         */
        private Set<String> embeddedEntities = new HashSet<String>();

        /**
         * set of properties that map to mappedEntities
         */
        private Set<String> mappedEntities = new HashSet<String>();

        /**
         * set of properties that map to element collections
         */
        private Set<String> elementCollections = new HashSet<String>();

        /**
         * the column family definition
         */
        private CfDef columnFamilyDefinition;

        /**
         * set of properties that map to counter properties
         */
        private Set<String> counterProperties = new HashSet<String>();

        /**
         * set of properties that map to properties that increase counter values
         */
        private Map<String, String> counterPropertiesIncrease = new HashMap<String, String>();

        /**
         * set of properties that map to mapped properties
         */
        private Set<String> mappedProperties = new HashSet<String>();

        /**
         * set of properties that map to mapped collections
         */
        private Set<String> mappedCollections = new HashSet<String>();

        /**
         * map of methods and their properties that are lazy accessors
         */
        private Map<Method, String> lazyAccesors = new HashMap<Method, String>();

        /**
         * set of properties that map to properties that should be loaded on demand when their read method is invoked
         */
        private Set<String> lazyProperties = new HashSet<String>();

        /**
         * a proxy method handler
         */
        private MethodHandler proxyMethodHandler;

        /**
         * the proxy class
         */
        private Class<?> proxyClass;

        /**
         * true if this metadata object is associated with a class that represents a Counter style column family
         */
        private boolean counterColumnFamily;

        /**
         * map of properties and the container classes associated
         */
        private Map<String, Class<?>> propertyContainerMap = new HashMap<String, Class<?>>();

        /**
         * map of events and the methods that act as listeners of those events
         */
        private Map<Event.Entity, Set<Method>> entityEventListenersMap = new TreeMap<Event.Entity, Set<Method>>();

        /**
         * the keyspace consistency level
         */
        private ConsistencyLevel consistencyLevel;

        /* Static Methods */

        /**
         * Gets a named query by its name
         *
         * @param name the query name
         * @return the query value
         */
        public static String getNamedQuery(String name) {
            String query = getNullSafeNamedQuery(name);
            if (query == null)
                throw new IllegalArgumentException(String.format("named query not found for name: %s", name));
            return query;
        }

        /**
         * Gets a name query by its name returning null if not found
         *
         * @param name the query name
         * @return the query value
         */
        public static String getNullSafeNamedQuery(String name) {
            return namedQueries.get(name);
        }

        /* Constructors */

        /**
         * Constructor.
         * Extracts and caches metadata for persistent entity classes
         *
         * @param target             the target class
         * @param persistenceFactory the persistence factory managing the entity
         */
        public ClassMeta(Class<T> target, AbstractPersistenceFactory persistenceFactory)
                throws ClassNotFoundException, IntrospectionException, InstantiationException, IllegalAccessException {
            log.debug(String.format("Initializing class metadata for %s", target));
            this.target = target;
            //If this is a top level structure that holds a column family
            if (target.isAnnotationPresent(ColumnFamily.class)) {
                ColumnFamily columnFamilyAnnotation = target.getAnnotation(ColumnFamily.class);
                consistencyLevel = columnFamilyAnnotation.consistencyLevel();
                counterColumnFamily = columnFamilyAnnotation.defaultValidationClass() == CounterColumnType.class;
                keySpace = StringUtils.defaultIfEmpty(columnFamilyAnnotation.keySpace(),
                        persistenceFactory.getDefaultKeySpace());
                columnFamily = target.getSimpleName();
                initializeColumnFamilyDefinition();
                processFields(target, "");
                processMethods(target);
                addClassTypePropertyIfSupported();
                initializeProxyFactory(persistenceFactory);
                initializeNamedQueries(target);
            } else {
                throw new IllegalArgumentException(target + " is not annotated with " + ColumnFamily.class);
            }
        }

        /**
         * Private helper to initialize a column family definition
         */
        private void initializeColumnFamilyDefinition() throws ClassNotFoundException {
            columnFamilyDefinition = new CfDef();
            columnFamilyDefinition.setName(columnFamily);
            columnFamilyDefinition.setKeyspace(getKeySpace());
            ColumnFamily cfAnnotation = target.getAnnotation(ColumnFamily.class);
            String comparatorType = cfAnnotation.compareWith().getName();
            if (cfAnnotation.reversed()) {
                comparatorType = String.format("%s(reversed=true)", comparatorType);
            }
            columnFamilyDefinition.setComparator_type(comparatorType);
            columnFamilyDefinition.setKey_cache_size(cfAnnotation.keysCached());
            columnFamilyDefinition.setRow_cache_size(cfAnnotation.rowsCached());
            columnFamilyDefinition.setComment(StringUtils.defaultIfEmpty(cfAnnotation.comment(), null));
            columnFamilyDefinition.setComparator_type(cfAnnotation.compareWith().getName());
            columnFamilyDefinition.setRead_repair_chance(cfAnnotation.readRepairChance());
            columnFamilyDefinition.setGc_grace_seconds(cfAnnotation.gcGraceSeconds());
            columnFamilyDefinition.setDefault_validation_class(
                    StringUtils.defaultIfEmpty(cfAnnotation.defaultValidationClass().getName(), null));
            columnFamilyDefinition.setKey_validation_class(
                    StringUtils.defaultIfEmpty(cfAnnotation.defaultKeyValidationClass().getName(), null));
            columnFamilyDefinition.setMin_compaction_threshold(cfAnnotation.minCompactionThreshold());
            columnFamilyDefinition.setMax_compaction_threshold(cfAnnotation.maxCompactionThreshold());
            columnFamilyDefinition.setReplicate_on_write(cfAnnotation.replicateOnWrite());
        }

        /**
         * Processes the target fields searching for persistent annotations
         *
         * @param target the target class
         * @param prefix a potential prefix utilized for deep nested properties
         */
        private void processFields(Class<?> target, String prefix)
                throws IntrospectionException, ClassNotFoundException {
            if (StringUtils.isNotBlank(prefix)) {
                prefix += ".";
            }
            for (Field field : ObjectUtils.getAllFieldsInHierarchy(target)) {
                processElement(field, prefix + field.getName(), field.getType());
            }
        }

        /**
         * Processes a field searching for persistent annotations
         *
         * @param element      the annotated element
         * @param propertyName the element name
         * @param type         the property type
         */
        private void processElement(Field element, String propertyName, Class<?> type)
                throws ClassNotFoundException, IntrospectionException {
            if (valid(element, propertyName, type)) {
                if (element.isAnnotationPresent(Embedded.class)) {
                    processEmbeddedEntity(type, propertyName);
                } else if (element.isAnnotationPresent(Mapped.class)) {
                    processMappedEntity(type, element, propertyName);
                } else if (element.isAnnotationPresent(MappedCollection.class)) {
                    processMappedCollection(type, element, propertyName);
                } else if (element.isAnnotationPresent(CounterIncrease.class)) {
                    processCounterIncrease(type, element, propertyName,
                            element.getAnnotation(CounterIncrease.class).value());
                } else {
                    if (element.isAnnotationPresent(Key.class)) {
                        keyProperty = propertyName;
                    }
                    processSimpleColumn(element, propertyName);
                }
            }
        }

        /**
         * Private helper.
         * Determines whether a field is valid for processing and persistence consideration
         *
         * @param element the field element
         * @param name    the name
         * @param type    the field type
         * @return if the field should be considered for persistence
         */
        private boolean valid(Field element, String name, Class<?> type) {
            return !name.equals(CLASS_PROPERTY) && !(element.isAnnotationPresent(Transient.class))
                    && !name.equals(keyProperty) && !type.getName().equals(PersistenceFactory.class.getName())
                    && !Modifier.isStatic(element.getModifiers());
        }

        /**
         * Processes metadata for a nested embedded association. Helper method
         *
         * @param type         the type
         * @param propertyName the property name
         */
        private void processEmbeddedEntity(Class<?> type, String propertyName)
                throws ClassNotFoundException, IntrospectionException {
            embeddedEntities.add(propertyName);
            propertiesTypesMap.put(propertyName, type);
            mutationProperties.add(propertyName);
            propertyContainerMap.put(propertyName, type);
            log.debug(String.format("added type %s and property %s", type.getName(), propertyName));
            processFields(type, propertyName);
        }

        /**
         * Processes metadata for a nested mapped association. Helper method
         *
         * @param type         the type
         * @param element
         * @param propertyName the property name
         */
        private void processMappedEntity(Class<?> type, Field element, String propertyName)
                throws ClassNotFoundException, IntrospectionException {
            Mapped mapped = element.getAnnotation(Mapped.class);
            mappedEntities.add(propertyName);
            propertiesTypesMap.put(propertyName, type);
            mutationProperties.add(propertyName);
            propertyContainerMap.put(propertyName, type);
            mappedProperties.add(propertyName);
            boolean lazy = mapped != null && mapped.lazy();
            addProperty(null, propertyName, element.getType(), true, lazy, false, false);
            log.debug(String.format("added mapped type %s and property %s", type.getName(), propertyName));
        }

        /**
         * Private helper that caches information for a property for further persistence consideration
         *
         * @param colAnnotation   the column annotation
         * @param propertyName    the property name
         * @param type            the property type
         * @param indexed         whether the property should be indexed in the data store
         * @param lazy            if access to this property should be loaded on demand
         * @param counter         if this property represents a counter
         * @param counterIncrease if this property represents a value for a counter arithmetic operation
         */
        private void addProperty(Column colAnnotation, String propertyName, Class<?> type, boolean indexed,
                boolean lazy, boolean counter, boolean counterIncrease)
                throws ClassNotFoundException, IntrospectionException {
            propertiesTypesMap.put(propertyName, type);
            mutationProperties.add(propertyName);
            if (indexed) {
                indexedProperties.add(propertyName);
                log.debug(String.format("added indexed property %s", propertyName));
            }
            if (lazy) {
                PropertyDescriptor descriptor = new PropertyDescriptor(propertyName, target);
                lazyAccesors.put(descriptor.getReadMethod(), propertyName);
                lazyProperties.add(propertyName);
            }
            if (counter) {
                counterProperties.add(propertyName);
            }
            if (!counterIncrease) {
                selectionProperties.add(propertyName);
                addColumnToColumnFamilyDefinition(colAnnotation, propertyName, indexed);
            }
            log.debug(String.format("added property %s", propertyName));
        }

        /**
         * Private helper that adds a c olumn to a column family definition
         *
         * @param colAnnotation the column annotation
         * @param property      the property
         * @param indexed       if this property should be indexed in the datastore
         */
        private void addColumnToColumnFamilyDefinition(Column colAnnotation, String property, boolean indexed)
                throws ClassNotFoundException {
            if (!property.equals(keyProperty)) {
                boolean defaults = colAnnotation == null;
                ColumnDef columnDef = new ColumnDef();
                columnDef.setName(StringSerializer.get().toByteBuffer(property));
                columnDef.setValidation_class(
                        defaults ? org.firebrandocm.dao.annotations.Column.DEFAULTS.VALIDATION_CLASS.getName()
                                : colAnnotation.validationClass().getName());
                indexed = indexed || isMappedContainer(property);
                if (!indexed) {
                    indexed = defaults ? org.firebrandocm.dao.annotations.Column.DEFAULTS.INDEXED
                            : colAnnotation.indexed();
                }
                if (indexed) {
                    columnDef.setIndex_name(String.format("%s_%s_%s", keySpace, columnFamily, property));
                    columnDef.setIndex_type(defaults ? org.firebrandocm.dao.annotations.Column.DEFAULTS.INDEX_TYPE
                            : colAnnotation.indexType());
                }
                columnFamilyDefinition.addToColumn_metadata(columnDef);
            }
        }

        /**
         * Whether this property should be considered as a mapped container which data belongs in some other row
         *
         * @param property the property
         * @return if this property is a mapped container
         */
        public boolean isMappedContainer(String property) {
            return mappedEntities.contains(property);
        }

        /**
         * Processes metadata for a nested mapped collection. Helper method
         *
         * @param type
         * @param element
         * @param propertyName
         * @throws ClassNotFoundException
         * @throws IntrospectionException
         */
        private void processMappedCollection(Class<?> type, Field element, String propertyName)
                throws ClassNotFoundException, IntrospectionException {
            MappedCollection mappedCollection = element.getAnnotation(MappedCollection.class);
            mappedCollections.add(propertyName);
            propertiesTypesMap.put(propertyName, type);
            mutationProperties.add(propertyName);
            propertyContainerMap.put(propertyName, type);
            mappedProperties.add(propertyName);
            boolean lazy = mappedCollection != null && mappedCollection.lazy();
            addProperty(null, propertyName, element.getType(), true, lazy, false, false);
            log.debug(String.format("added mapped type %s and property %s", type.getName(), propertyName));
        }

        /**
         * Processes metadata for counter increase property
         *
         * @param type
         * @param element
         * @param propertyName
         * @throws ClassNotFoundException
         * @throws IntrospectionException
         */
        private void processCounterIncrease(Class<?> type, Field element, String propertyName, String targetCounter)
                throws ClassNotFoundException, IntrospectionException {
            counterPropertiesIncrease.put(propertyName, targetCounter);
            addProperty(null, propertyName, element.getType(), true, false, false, true);
            log.debug(String.format("added processCounterIncrease type %s and property %s", type.getName(),
                    propertyName));
        }

        /**
         * Processes metadata for a simple column. Helper method
         *
         * @param element      the type
         * @param propertyName the property name
         */
        private void processSimpleColumn(Field element, String propertyName)
                throws ClassNotFoundException, IntrospectionException {
            propertiesTypesMap.put(propertyName, element.getType());
            mutationProperties.add(propertyName);
            propertyContainerMap.put(propertyName, element.getDeclaringClass());
            boolean indexed = false;
            if (element.isAnnotationPresent(Column.class)) {
                Column columnAnnotation = element.getAnnotation(Column.class);
                indexed = columnAnnotation != null && columnAnnotation.indexed();
            }
            org.firebrandocm.dao.annotations.Column colAnnotation = element
                    .getAnnotation(org.firebrandocm.dao.annotations.Column.class);
            boolean lazy = colAnnotation != null && colAnnotation.lazy();
            boolean counter = colAnnotation != null && colAnnotation.counter();
            addProperty(colAnnotation, propertyName, element.getType(), indexed, lazy, counter, false);
            log.debug(String.format("added property %s", propertyName));
        }

        /**
         * Private Helper.
         * Processes all methods in hierarchy for the annotated entities scanning for persistence annotations
         *
         * @param target the target class
         */
        private void processMethods(Class<T> target) {
            for (Method method : ObjectUtils.getAllMethodsInHierarchy(target)) {
                processMethod(method);
            }
        }

        /**
         * Private Helper.
         * Processes a method scanning for persistence annotations
         *
         * @param method the method
         */
        private void processMethod(Method method) {
            if (method.isAnnotationPresent(OnEvent.class)) {
                OnEvent onEvent = method.getAnnotation(OnEvent.class);
                Set<Method> methods = entityEventListenersMap.get(onEvent.value());
                if (methods == null) {
                    methods = new LinkedHashSet<Method>();
                    entityEventListenersMap.put(onEvent.value(), methods);
                }
                methods.add(method);
            }
        }

        /**
         * Private Helper.
         * Adds an internal class property to obtain class information from each inserted row
         */
        private void addClassTypePropertyIfSupported() throws ClassNotFoundException, IntrospectionException {
            if (!counterColumnFamily) {
                addProperty(null, PersistenceFactory.CLASS_PROPERTY, String.class, true, false, false, false);
            }
        }

        /**
         * Initializes a class proxy factory that enhances instances wrapping calls to lazy and other methods that need to be
         * audited around invokations
         *
         * @param persistenceFactory the persistence factory associated with this context
         */
        private void initializeProxyFactory(final AbstractPersistenceFactory persistenceFactory)
                throws IllegalAccessException, InstantiationException {
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.setSuperclass(target);
            proxyFactory.setFilter(new MethodFilter() {
                public boolean isHandled(Method m) {
                    return isLazyAccessor(m);
                }
            });
            proxyClass = proxyFactory.createClass();
            proxyMethodHandler = new MethodHandler() {
                public Object invoke(Object self, Method m, Method proceed, Object[] args) throws Throwable {
                    log.debug("lazy loading: " + m.getName());
                    persistenceFactory.loadLazyPropertyIfNecessary(ClassMeta.this, self, proceed, m, args);
                    return proceed.invoke(self, args); // execute the original method.
                }
            };
        }

        /**
         * Informs whether a method corresponds with a lazy accessor
         *
         * @param method the method
         * @return if it's the accessor of a lazy property
         */
        public boolean isLazyAccessor(Method method) {
            return lazyAccesors.containsKey(method);
        }

        /**
         * Private Helper. Scans for named query annotations an initializes the named queries associated with this persistence factory
         *
         * @param target the target class
         */
        private void initializeNamedQueries(Class<T> target) {
            List<NamedQuery> queries = new ArrayList<NamedQuery>();
            if (target.isAnnotationPresent(NamedQueries.class)) {
                NamedQuery[] value = target.getAnnotation(NamedQueries.class).value();
                for (int i = 0, valueLength = value.length; i < valueLength; i++) {
                    NamedQuery namedQuery = value[i];
                    queries.add(namedQuery);
                }
            }
            if (target.isAnnotationPresent(NamedQuery.class)) {
                queries.add(target.getAnnotation(NamedQuery.class));
            }
            for (NamedQuery query : queries) {
                if (namedQueries.containsKey(query.name())) {
                    throw new IllegalStateException(String.format("Duplicated named query name: %s", query.name()));
                }
                namedQueries.put(query.name(), query.query());
            }
        }

        /* Getters & Setters */

        /**
         * @return the column family
         */
        public String getColumnFamily() {
            return columnFamily;
        }

        /**
         * @return the column family definition
         */
        public CfDef getColumnFamilyDefinition() {
            return columnFamilyDefinition;
        }

        /**
         * @return the set of properties that declares themselves as being part of secondary indexes
         */
        public Set<String> getIndexedProperties() {
            return indexedProperties;
        }

        /**
         * @return the key property that holds the key for this class / columnfamily
         */
        public String getKeyProperty() {
            return keyProperty;
        }

        /**
         * @return the keyspace for this column family
         */
        public String getKeySpace() {
            return keySpace;
        }

        /**
         * @return the mapped properties
         */
        public Set<String> getMappedProperties() {
            return mappedProperties;
        }

        /**
         * @return the set of all persistent properties
         */
        public Set<String> getMutationProperties() {
            return mutationProperties;
        }

        /**
         * @return the map of properties and their types
         */
        public Map<String, Class<?>> getPropertiesTypesMap() {
            return propertiesTypesMap;
        }

        /**
         * @return the map of property containers and their type
         */
        public Map<String, Class<?>> getPropertyContainerMap() {
            return propertyContainerMap;
        }

        /**
         * @return the properties that will be considered on selection operation
         */
        public Set<String> getSelectionProperties() {
            return selectionProperties;
        }

        /**
         * @return the target
         */
        public Class<T> getTarget() {
            return target;
        }

        /**
         * @return true if the metadata is associated with a counter column family
         */
        public boolean isCounterColumnFamily() {
            return counterColumnFamily;
        }

        /* Canonical Methods */

        /**
         * @see Object#toString()
         */
        @Override
        public String toString() {
            return "ClassMeta{" + "target=" + target.getName() + '}';
        }

        /* Misc */

        /**
         * Creates a proxy instance for the class represented in this metadata
         *
         * @return the proxy class
         */
        @SuppressWarnings("unchecked")
        public T createProxy() {
            T instance;
            try {
                instance = (T) proxyClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
            ((ProxyObject) instance).setHandler(proxyMethodHandler);
            return instance;
        }

        /**
         * destroys and frees any resources retained by this metadata
         */
        public void destroy() {
            namedQueries.clear();
        }

        /**
         * @param name the column name
         * @return the java type associated to the column
         */
        public Class<?> getColumnClass(String name) {
            return propertiesTypesMap.get(name);
        }

        /**
         * Gets a lazy property associated to a given a method
         *
         * @param method the method
         * @return the lazy property if found, null otherwise
         */
        public String getLazyProperty(Method method) {
            return lazyAccesors.get(method);
        }

        /**
         * Gets a method associated to a given event
         *
         * @param event the event
         * @return the method if found, null otherwise
         */
        public Set<Method> getListenersForEvent(Event.Entity event) {
            return entityEventListenersMap.get(event);
        }

        /**
         * Gets a counter property associated to an increase counter property
         *
         * @param increaseCounterProperty the increase counter property
         * @return the counter property if found, null otherwise
         */
        public String getTargetCounterProperty(String increaseCounterProperty) {
            return counterPropertiesIncrease.get(increaseCounterProperty);
        }

        /**
         * Checks if a property is a container (embedded, mapped or collection)
         *
         * @param property the property
         * @return true if the property is a container
         */
        public boolean isContainer(String property) {
            return isAssociationContainer(property) || isMappedContainer(property) || isMappedCollection(property);
        }

        /**
         * Checks if the property is a embedded container
         *
         * @param property the property
         * @return true if the property is an embedded container
         */
        public boolean isAssociationContainer(String property) {
            return embeddedEntities.contains(property);
        }

        /**
         * Checks if the property is a mapped collection
         *
         * @param property the property
         * @return true if the property is a mapped collection
         */
        public boolean isMappedCollection(String property) {
            return mappedCollections.contains(property);
        }

        /**
         * Checks if the property is a counter increase property.
         * A property that is used to increase counter columns
         *
         * @param property the property
         * @return true if the property is a counter increase property
         */
        public boolean isCounterIncreaseProperty(String property) {
            return counterPropertiesIncrease.containsKey(property);
        }

        /**
         * Checks if the property is a counter property
         * @param property the property
         * @return true if the property is a counter property
         */
        public boolean isCounterProperty(String property) {
            return counterProperties.contains(property);
        }

        /**
         * Checks if the property is an element collection e.g. List<? not an entity>
         * @param property the property
         * @return true if the property is an element collection
         */
        public boolean isElementCollection(String property) {
            return elementCollections.contains(property);
        }

        /**
         * Check if the property is flagged as lazy and should be loaded when it's getter is invoked
         * @param property the property
         * @return true if the property is flagged as a lazy property
         */
        public boolean isLazyProperty(String property) {
            return lazyProperties.contains(property);
        }

        /**
         *
         * @return the keyspace consistency level
         */
        public ConsistencyLevel getConsistencyLevel() {
            return consistencyLevel;
        }
    }