You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by sn...@apache.org on 2014/01/28 23:21:50 UTC

[40/96] [abbrv] [partial] Change package namespace to org.apache.usergrid

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/2c2acbe4/stack/core/src/main/java/org/apache/usergrid/persistence/Schema.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/Schema.java b/stack/core/src/main/java/org/apache/usergrid/persistence/Schema.java
new file mode 100644
index 0000000..7f24079
--- /dev/null
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/Schema.java
@@ -0,0 +1,1786 @@
+/*******************************************************************************
+ * Copyright 2012 Apigee Corporation
+ *
+ * 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.apache.usergrid.persistence;
+
+
+import java.beans.PropertyDescriptor;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.node.ObjectNode;
+import org.codehaus.jackson.smile.SmileFactory;
+import org.codehaus.jackson.type.TypeReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.lang.reflect.FieldUtils;
+import org.apache.usergrid.persistence.annotations.EntityCollection;
+import org.apache.usergrid.persistence.annotations.EntityDictionary;
+import org.apache.usergrid.persistence.annotations.EntityProperty;
+import org.apache.usergrid.persistence.cassandra.CassandraPersistenceUtils;
+import org.apache.usergrid.persistence.entities.Application;
+import org.apache.usergrid.persistence.exceptions.PropertyTypeConversionException;
+import org.apache.usergrid.persistence.schema.CollectionInfo;
+import org.apache.usergrid.persistence.schema.DictionaryInfo;
+import org.apache.usergrid.persistence.schema.EntityInfo;
+import org.apache.usergrid.persistence.schema.PropertyInfo;
+import org.apache.usergrid.utils.InflectionUtils;
+import org.apache.usergrid.utils.JsonUtils;
+import org.apache.usergrid.utils.MapUtils;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+import me.prettyprint.hector.api.beans.ColumnSlice;
+import me.prettyprint.hector.api.beans.HColumn;
+import me.prettyprint.hector.api.beans.Row;
+import static org.apache.commons.lang.StringUtils.isNotBlank;
+import static org.apache.usergrid.utils.ConversionUtils.bytebuffer;
+import static org.apache.usergrid.utils.ConversionUtils.string;
+import static org.apache.usergrid.utils.ConversionUtils.uuid;
+import static org.apache.usergrid.utils.InflectionUtils.pluralize;
+import static org.apache.usergrid.utils.InflectionUtils.singularize;
+import static org.apache.usergrid.utils.JsonUtils.toJsonNode;
+import static org.apache.usergrid.utils.MapUtils.hashMap;
+import static org.apache.usergrid.utils.StringUtils.stringOrSubstringAfterLast;
+
+
+/**
+ * The controller class for determining Entity relationships as well as properties types. This class loads the entity
+ * schema definition from a YAML file called usergrid-schema.yaml at the root of the classpath.
+ *
+ * @author edanuff
+ */
+public class Schema {
+
+    private static final Logger logger = LoggerFactory.getLogger( Schema.class );
+
+    public static final String DEFAULT_ENTITIES_PACKAGE = "org.apache.usergrid.persistence.entities";
+
+    public static final String TYPE_APPLICATION = "application";
+    public static final String TYPE_ENTITY = "entity";
+    public static final String TYPE_ROLE = "role";
+    public static final String TYPE_CONNECTION = "connection";
+    public static final String TYPE_MEMBER = "member";
+
+    public static final String PROPERTY_ACTIVATED = "activated";
+    public static final String PROPERTY_COLLECTION_NAME = "collectionName";
+    public static final String PROPERTY_CREATED = "created";
+    public static final String PROPERTY_CONFIRMED = "confirmed";
+    public static final String PROPERTY_DISABLED = "disabled";
+    public static final String PROPERTY_UUID = "uuid";
+    public static final String PROPERTY_EMAIL = "email";
+    public static final String PROPERTY_ITEM = "item";
+    public static final String PROPERTY_ITEM_TYPE = "itemType";
+    public static final String PROPERTY_MEMBERSHIP = "membership";
+    public static final String PROPERTY_METADATA = "metadata";
+    public static final String PROPERTY_MODIFIED = "modified";
+    public static final String PROPERTY_NAME = "name";
+    public static final String PROPERTY_OWNER = "owner";
+    public static final String PROPERTY_OWNER_TYPE = "ownerType";
+    public static final String PROPERTY_PATH = "path";
+    public static final String PROPERTY_PICTURE = "picture";
+    public static final String PROPERTY_PUBLISHED = "published";
+    public static final String PROPERTY_SECRET = "secret";
+    public static final String PROPERTY_TIMESTAMP = "timestamp";
+    public static final String PROPERTY_TITLE = "title";
+    public static final String PROPERTY_TYPE = "type";
+    public static final String PROPERTY_URI = "uri";
+    public static final String PROPERTY_USERNAME = "username";
+    public static final String PROPERTY_INACTIVITY = "inactivity";
+
+    public static final String PROPERTY_CONNECTION = "connection";
+    public static final String PROPERTY_ASSOCIATED = "associated";
+    public static final String PROPERTY_CURSOR = "cursor";
+
+    public static final String COLLECTION_ROLES = "roles";
+    public static final String COLLECTION_USERS = "users";
+    public static final String COLLECTION_GROUPS = "groups";
+
+    public static final String INDEX_COLLECTIONS = "collections";
+    public static final String INDEX_CONNECTIONS = "connections";
+
+    public static final String DICTIONARY_PROPERTIES = "properties";
+    public static final String DICTIONARY_SETS = "sets";
+    public static final String DICTIONARY_COLLECTIONS = "collections";
+    public static final String DICTIONARY_CONNECTIONS = "connections";
+    public static final String DICTIONARY_INDEXES = "indexes";
+    public static final String DICTIONARY_CONNECTING_TYPES = "connecting_types";
+    public static final String DICTIONARY_CONNECTING_ENTITIES = "connecting_entities";
+    public static final String DICTIONARY_CONNECTED_TYPES = "connected_types";
+    public static final String DICTIONARY_CONNECTED_ENTITIES = "connected_entities";
+    public static final String DICTIONARY_CONTAINER_ENTITIES = "container_entities";
+    public static final String DICTIONARY_CREDENTIALS = "credentials";
+    public static final String DICTIONARY_ROLENAMES = "rolenames";
+    public static final String DICTIONARY_ROLETIMES = "roletimes";
+    public static final String DICTIONARY_PERMISSIONS = "permissions";
+    public static final String DICTIONARY_ID_SETS = "id_sets";
+    public static final String DICTIONARY_COUNTERS = "counters";
+    public static final String DICTIONARY_GEOCELL = "geocell";
+
+    private static List<String> entitiesPackage = new ArrayList<String>();
+    private static List<String> entitiesScanPath = new ArrayList<String>();
+
+    @SuppressWarnings("rawtypes")
+    public static Map<String, Class> DEFAULT_DICTIONARIES =
+            hashMap( DICTIONARY_PROPERTIES, ( Class ) String.class ).map( DICTIONARY_SETS, String.class )
+                    .map( DICTIONARY_INDEXES, String.class ).map( DICTIONARY_COLLECTIONS, String.class )
+                    .map( DICTIONARY_CONNECTIONS, String.class ).map( DICTIONARY_CONNECTING_TYPES, String.class )
+                    .map( DICTIONARY_CONNECTING_ENTITIES, String.class ).map( DICTIONARY_CONNECTED_TYPES, String.class )
+                    .map( DICTIONARY_CONNECTED_ENTITIES, String.class )
+                    .map( DICTIONARY_CONTAINER_ENTITIES, String.class )
+                    .map( DICTIONARY_CREDENTIALS, CredentialsInfo.class ).map( DICTIONARY_ROLENAMES, String.class )
+                    .map( DICTIONARY_ROLETIMES, Long.class ).map( DICTIONARY_PERMISSIONS, String.class )
+                    .map( DICTIONARY_ID_SETS, String.class );
+
+    private static LoadingCache<String, String> baseEntityTypes =
+            CacheBuilder.newBuilder().expireAfterAccess( 10, TimeUnit.MINUTES )
+                        .build( new CacheLoader<String, String>() {
+                            public String load( String key ) { // no checked exception
+                                return createNormalizedEntityType( key, true );
+                            }
+                        } );
+
+    private static LoadingCache<String, String> nonbaseEntityTypes =
+            CacheBuilder.newBuilder().expireAfterAccess( 10, TimeUnit.MINUTES )
+                        .build( new CacheLoader<String, String>() {
+                            public String load( String key ) { // no checked exception
+                                return createNormalizedEntityType( key, false );
+                            }
+                        } );
+
+    private static LoadingCache<String, String> collectionNameCache =
+            CacheBuilder.newBuilder().maximumSize( 1000 ).build( new CacheLoader<String, String>() {
+                public String load( String key ) { // no checked exception
+                    return _defaultCollectionName( key );
+                }
+            } );
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    @SuppressWarnings("unused")
+    private final SmileFactory smile = new SmileFactory();
+
+    private final Map<String, Class<? extends Entity>> typeToEntityClass =
+            new ConcurrentHashMap<String, Class<? extends Entity>>();
+
+    private final Map<Class<? extends Entity>, String> entityClassToType =
+            new ConcurrentHashMap<Class<? extends Entity>, String>();
+
+    private final Map<Class<? extends Entity>, Map<String, PropertyDescriptor>> entityClassPropertyToDescriptor =
+            new ConcurrentHashMap<Class<? extends Entity>, Map<String, PropertyDescriptor>>();
+
+    private final Map<Class<? extends Entity>, EntityInfo> registeredEntityClasses =
+            new ConcurrentHashMap<Class<? extends Entity>, EntityInfo>();
+
+    Map<String, EntityInfo> entityMap = new TreeMap<String, EntityInfo>( String.CASE_INSENSITIVE_ORDER );
+    ;
+
+    Map<String, Map<String, Set<CollectionInfo>>> entityContainerCollections =
+            new TreeMap<String, Map<String, Set<CollectionInfo>>>( String.CASE_INSENSITIVE_ORDER );
+
+    Map<String, Map<String, Set<CollectionInfo>>> entityContainerCollectionsIndexingProperties =
+            new TreeMap<String, Map<String, Set<CollectionInfo>>>( String.CASE_INSENSITIVE_ORDER );
+    Map<String, Map<String, Set<CollectionInfo>>> entityContainerCollectionsIndexingDictionaries =
+            new TreeMap<String, Map<String, Set<CollectionInfo>>>( String.CASE_INSENSITIVE_ORDER );
+    Map<String, Map<String, Set<CollectionInfo>>> entityContainerCollectionsIndexingDynamicDictionaries =
+            new TreeMap<String, Map<String, Set<CollectionInfo>>>( String.CASE_INSENSITIVE_ORDER );
+
+    Map<String, Map<String, Map<String, Set<CollectionInfo>>>> entityPropertyContainerCollectionsIndexingProperty =
+            new TreeMap<String, Map<String, Map<String, Set<CollectionInfo>>>>( String.CASE_INSENSITIVE_ORDER );
+
+    Map<String, Map<String, Map<String, Set<CollectionInfo>>>> entityDictionaryContainerCollectionsIndexingDictionary =
+            new TreeMap<String, Map<String, Map<String, Set<CollectionInfo>>>>( String.CASE_INSENSITIVE_ORDER );
+
+    Map<String, PropertyInfo> allIndexedProperties = new TreeMap<String, PropertyInfo>( String.CASE_INSENSITIVE_ORDER );
+
+    Map<String, PropertyInfo> allProperties = new TreeMap<String, PropertyInfo>( String.CASE_INSENSITIVE_ORDER );
+
+    private static Schema instance;
+
+    boolean initialized = false;
+
+
+    public Schema() {
+        setDefaultSchema( this );
+
+        mapper.configure( SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false );
+    }
+
+
+    public static final Object initLock = new Object();
+
+
+    public static void setDefaultSchema( Schema instance ) {
+        synchronized ( initLock ) {
+            if ( Schema.instance == null ) {
+                Schema.instance = instance;
+            }
+        }
+    }
+
+
+    public static Schema getDefaultSchema() {
+        if ( instance == null ) {
+            synchronized ( initLock ) {
+                if ( instance == null ) {
+                    logger.info( "Initializing schema..." );
+                    instance = new Schema();
+                    instance.init();
+                    logger.info( "Schema initialized" );
+                }
+            }
+        }
+        return instance;
+    }
+
+
+    public void mapCollector( String entityType, String containerType, String collectionName,
+                              CollectionInfo collection ) {
+
+        MapUtils.addMapMapSet( entityContainerCollections, true, entityType, containerType, collection );
+
+        if ( !collection.getPropertiesIndexed().isEmpty() ) {
+            MapUtils.addMapMapSet( entityContainerCollectionsIndexingProperties, true, entityType, containerType,
+                    collection );
+            for ( String propertyName : collection.getPropertiesIndexed() ) {
+                MapUtils.addMapMapMapSet( entityPropertyContainerCollectionsIndexingProperty, true, entityType,
+                        propertyName, containerType, collection );
+            }
+        }
+
+        if ( !collection.getDictionariesIndexed().isEmpty() ) {
+            MapUtils.addMapMapSet( entityContainerCollectionsIndexingDictionaries, true, entityType, containerType,
+                    collection );
+            for ( String dictionaryName : collection.getDictionariesIndexed() ) {
+                MapUtils.addMapMapMapSet( entityDictionaryContainerCollectionsIndexingDictionary, true, entityType,
+                        dictionaryName, containerType, collection );
+            }
+        }
+
+        if ( collection.isIndexingDynamicDictionaries() ) {
+            MapUtils.addMapMapSet( entityContainerCollectionsIndexingDynamicDictionaries, true, entityType,
+                    containerType, collection );
+        }
+    }
+
+
+    private <T extends Annotation> T getAnnotation( Class<? extends Entity> entityClass, PropertyDescriptor descriptor,
+                                                    Class<T> annotationClass ) {
+        try {
+            if ( ( descriptor.getReadMethod() != null ) && descriptor.getReadMethod()
+                                                                     .isAnnotationPresent( annotationClass ) ) {
+                return descriptor.getReadMethod().getAnnotation( annotationClass );
+            }
+            if ( ( descriptor.getWriteMethod() != null ) && descriptor.getWriteMethod()
+                                                                      .isAnnotationPresent( annotationClass ) ) {
+                return descriptor.getWriteMethod().getAnnotation( annotationClass );
+            }
+            Field field = FieldUtils.getField( entityClass, descriptor.getName(), true );
+            if ( field != null ) {
+                if ( field.isAnnotationPresent( annotationClass ) ) {
+                    return field.getAnnotation( annotationClass );
+                }
+            }
+        }
+        catch ( Exception e ) {
+            logger.error( "Could not retrieve the annotations", e );
+        }
+        return null;
+    }
+
+
+    public synchronized void registerEntity( Class<? extends Entity> entityClass ) {
+        logger.info( "Registering {}", entityClass );
+        EntityInfo e = registeredEntityClasses.get( entityClass );
+        if ( e != null ) {
+            return;
+        }
+
+        Map<String, PropertyDescriptor> propertyDescriptors = entityClassPropertyToDescriptor.get( entityClass );
+        if ( propertyDescriptors == null ) {
+            EntityInfo entity = new EntityInfo();
+
+            String type = getEntityType( entityClass );
+
+            propertyDescriptors = new LinkedHashMap<String, PropertyDescriptor>();
+            Map<String, PropertyInfo> properties = new TreeMap<String, PropertyInfo>( String.CASE_INSENSITIVE_ORDER );
+            Map<String, CollectionInfo> collections =
+                    new TreeMap<String, CollectionInfo>( String.CASE_INSENSITIVE_ORDER );
+            Map<String, DictionaryInfo> sets = new TreeMap<String, DictionaryInfo>( String.CASE_INSENSITIVE_ORDER );
+
+            PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors( entityClass );
+
+            for ( PropertyDescriptor descriptor : descriptors ) {
+                String name = descriptor.getName();
+
+                EntityProperty propertyAnnotation = getAnnotation( entityClass, descriptor, EntityProperty.class );
+                if ( propertyAnnotation != null ) {
+                    if ( isNotBlank( propertyAnnotation.name() ) ) {
+                        name = propertyAnnotation.name();
+                    }
+                    propertyDescriptors.put( name, descriptor );
+
+                    PropertyInfo propertyInfo = new PropertyInfo( propertyAnnotation );
+                    propertyInfo.setName( name );
+                    propertyInfo.setType( descriptor.getPropertyType() );
+
+                    properties.put( name, propertyInfo );
+                    // logger.info(propertyInfo);
+                }
+
+                EntityCollection collectionAnnotation =
+                        getAnnotation( entityClass, descriptor, EntityCollection.class );
+                if ( collectionAnnotation != null ) {
+                    CollectionInfo collectionInfo = new CollectionInfo( collectionAnnotation );
+                    collectionInfo.setName( name );
+                    collectionInfo.setContainer( entity );
+
+                    collections.put( name, collectionInfo );
+                    // logger.info(collectionInfo);
+                }
+
+                EntityDictionary setAnnotation = getAnnotation( entityClass, descriptor, EntityDictionary.class );
+                if ( setAnnotation != null ) {
+                    DictionaryInfo setInfo = new DictionaryInfo( setAnnotation );
+                    setInfo.setName( name );
+                    // setInfo.setType(descriptor.getPropertyType());
+                    sets.put( name, setInfo );
+                    // logger.info(setInfo);
+                }
+            }
+
+            if ( !DynamicEntity.class.isAssignableFrom( entityClass ) ) {
+                entity.setProperties( properties );
+                entity.setCollections( collections );
+                entity.setDictionaries( sets );
+                entity.mapCollectors( this, type );
+
+                entityMap.put( type, entity );
+
+                allProperties.putAll( entity.getProperties() );
+
+                Set<String> propertyNames = entity.getIndexedProperties();
+                for ( String propertyName : propertyNames ) {
+                    PropertyInfo property = entity.getProperty( propertyName );
+                    if ( ( property != null ) && !allIndexedProperties.containsKey( propertyName ) ) {
+                        allIndexedProperties.put( propertyName, property );
+                    }
+                }
+            }
+
+            entityClassPropertyToDescriptor.put( entityClass, propertyDescriptors );
+
+            registeredEntityClasses.put( entityClass, entity );
+        }
+    }
+
+
+    public synchronized void init() {
+        if ( !initialized ) {
+            initialized = true;
+            addEntitiesPackage( DEFAULT_ENTITIES_PACKAGE );
+            scanEntities();
+        }
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public void scanEntities() {
+        synchronized ( entitiesScanPath ) {
+            for ( String path : entitiesScanPath ) {
+                ClassPathScanningCandidateComponentProvider provider =
+                        new ClassPathScanningCandidateComponentProvider( true );
+                provider.addIncludeFilter( new AssignableTypeFilter( TypedEntity.class ) );
+
+                Set<BeanDefinition> components = provider.findCandidateComponents( path );
+                for ( BeanDefinition component : components ) {
+                    try {
+                        Class<?> cls = Class.forName( component.getBeanClassName() );
+                        if ( Entity.class.isAssignableFrom( cls ) ) {
+                            registerEntity( ( Class<? extends Entity> ) cls );
+                        }
+                    }
+                    catch ( ClassNotFoundException e ) {
+                        logger.error( "Unable to get entity class ", e );
+                    }
+                }
+                registerEntity( DynamicEntity.class );
+            }
+        }
+    }
+
+
+    public void addEntitiesPackage( String entityPackage ) {
+        if ( !entitiesPackage.contains( entityPackage ) ) {
+            entitiesPackage.add( entityPackage );
+            String path = entityPackage.replaceAll( "\\.", "/" );
+            synchronized ( entitiesScanPath ) {
+                entitiesScanPath.add( path );
+            }
+        }
+    }
+
+
+    public void removeEntitiesPackage( String entityPackage ) {
+        entitiesPackage.remove( entityPackage );
+        String path = entityPackage.replaceAll( "\\.", "/" );
+        synchronized ( entitiesScanPath ) {
+            entitiesScanPath.remove( path );
+        }
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public List<String> getEntitiesPackage() {
+        return ( List<String> ) ( ( ArrayList<String> ) entitiesPackage ).clone();
+    }
+
+
+    /** @return value */
+    public Map<String, PropertyInfo> getAllIndexedProperties() {
+
+        return allIndexedProperties;
+    }
+
+
+    public Set<String> getAllIndexedPropertyNames() {
+
+        return allIndexedProperties.keySet();
+    }
+
+
+    public Set<String> getAllPropertyNames() {
+
+        return allProperties.keySet();
+    }
+
+
+    public String[] getAllPropertyNamesAsArray() {
+
+        return allProperties.keySet().toArray( new String[0] );
+    }
+
+
+    /** @return value */
+    public EntityInfo getEntityInfo( String entityType ) {
+
+        if ( entityType == null ) {
+            return null;
+        }
+
+        entityType = normalizeEntityType( entityType );
+
+        if ( "dynamicentity".equalsIgnoreCase( entityType ) ) {
+            throw new IllegalArgumentException( entityType + " is not a valid entity type" );
+        }
+
+        EntityInfo entity = entityMap.get( entityType );
+        if ( entity == null ) {
+            return getDynamicEntityInfo( entityType );
+        }
+        return entity;
+    }
+
+
+    public JsonNode getEntityJsonSchema( String entityType ) {
+        Class<?> cls = getEntityClass( entityType );
+        if ( cls == null ) {
+            cls = DynamicEntity.class;
+        }
+        try {
+            JsonNode schemaNode = mapper.generateJsonSchema( cls ).getSchemaNode();
+            if ( schemaNode != null ) {
+                JsonNode properties = schemaNode.get( "properties" );
+                if ( properties instanceof ObjectNode ) {
+                    Set<String> fieldsToRemove = new LinkedHashSet<String>();
+                    Iterator<String> i = properties.getFieldNames();
+                    while ( i.hasNext() ) {
+                        String propertyName = i.next();
+                        if ( !hasProperty( entityType, propertyName ) ) {
+                            fieldsToRemove.add( propertyName );
+                        }
+                        else {
+                            ObjectNode property = ( ObjectNode ) properties.get( propertyName );
+                            if ( isRequiredProperty( entityType, propertyName ) ) {
+                                property.put( "optional", false );
+                            }
+                        }
+                    }
+                    ( ( ObjectNode ) properties ).remove( fieldsToRemove );
+                }
+            }
+            return schemaNode;
+        }
+        catch ( Exception e ) {
+            logger.error( "Unable to get schema for entity type " + entityType, e );
+        }
+        return null;
+    }
+
+
+    public String getEntityType( Class<? extends Entity> cls ) {
+        String type = entityClassToType.get( cls );
+        if ( type != null ) {
+            return type;
+        }
+
+        String className = cls.getName();
+        boolean finded = false;
+        for ( String entityPackage : entitiesPackage ) {
+            String entityPackagePrefix = entityPackage + ".";
+            if ( className.startsWith( entityPackagePrefix ) ) {
+                type = className.substring( entityPackagePrefix.length() );
+                type = InflectionUtils.underscore( type );
+                finded = true;
+            }
+        }
+
+        if ( !finded ) {
+            type = className;
+        }
+
+        typeToEntityClass.put( type, cls );
+        entityClassToType.put( cls, type );
+        return type;
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private Class<? extends Entity> entityClassForName( String className ) {
+        try {
+            @SuppressWarnings("rawtypes") Class cls = Class.forName( className );
+            if ( Entity.class.isAssignableFrom( cls ) ) {
+                return cls;
+            }
+        }
+        catch ( ClassNotFoundException e ) {
+        }
+        return null;
+    }
+
+
+    public Class<? extends Entity> getEntityClass( String type ) {
+        type = getAssociatedEntityType( type );
+        Class<? extends Entity> cls = typeToEntityClass.get( type );
+        if ( cls != null ) {
+            return cls;
+        }
+
+        for ( String entityPackage : entitiesPackage ) {
+            String entityPackagePrefix = entityPackage + ".";
+            cls = entityClassForName( entityPackagePrefix + InflectionUtils.camelCase( type, true ) );
+            if ( cls == null ) {
+                cls = entityClassForName( entityPackagePrefix + type );
+            }
+
+            if ( cls == null ) {
+                cls = entityClassForName( type );
+            }
+
+            if ( cls != null ) {
+                break;
+            }
+        }
+
+        if ( cls == null ) {
+            cls = DynamicEntity.class;
+        }
+
+        typeToEntityClass.put( type, cls );
+        entityClassToType.put( cls, type );
+        return cls;
+    }
+
+
+    /** @return value */
+    public boolean hasProperties( String entityType ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        return entity.hasProperties();
+    }
+
+
+    /** @return value */
+    public Set<String> getPropertyNames( String entityType ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return null;
+        }
+
+        return entity.getProperties().keySet();
+    }
+
+
+    /** @return value */
+    public String[] getPropertyNamesAsArray( String entityType ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return new String[0];
+        }
+
+        return entity.getProperties().keySet().toArray( new String[0] );
+    }
+
+
+    /** @return value */
+    public boolean hasProperty( String entityType, String propertyName ) {
+
+        if ( propertyName.equals( PROPERTY_UUID ) || propertyName.equals( PROPERTY_TYPE ) ) {
+            return true;
+        }
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        return entity.hasProperty( propertyName );
+    }
+
+
+    public String aliasProperty( String entityType ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return null;
+        }
+
+        return entity.getAliasProperty();
+    }
+
+
+    /** @return value */
+    public boolean isPropertyMutable( String entityType, String propertyName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        return entity.isPropertyMutable( propertyName );
+    }
+
+
+    public boolean isPropertyUnique( String entityType, String propertyName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        return entity.isPropertyUnique( propertyName );
+    }
+
+
+    public boolean isPropertyIndexed( String entityType, String propertyName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return true;
+        }
+
+        if ( entity.hasProperty( propertyName ) ) {
+            return entity.isPropertyIndexed( propertyName );
+        }
+
+        return true;
+    }
+
+
+    public boolean isPropertyFulltextIndexed( String entityType, String propertyName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return true;
+        }
+
+        if ( entity.hasProperty( propertyName ) ) {
+            return entity.isPropertyFulltextIndexed( propertyName );
+        }
+
+        return true;
+    }
+
+
+    public boolean isPropertyTimestamp( String entityType, String propertyName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        return entity.isPropertyTimestamp( propertyName );
+    }
+
+
+    /** @return value */
+    public Set<String> getRequiredProperties( String entityType ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return null;
+        }
+
+        return entity.getRequiredProperties();
+    }
+
+
+    /** @return value */
+    public boolean isRequiredProperty( String entityType, String propertyName ) {
+
+        if ( propertyName.equals( PROPERTY_UUID ) || propertyName.equals( PROPERTY_TYPE ) ) {
+            return true;
+        }
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        return entity.isPropertyRequired( propertyName );
+    }
+
+
+    /** @return value */
+    public Class<?> getPropertyType( String entityType, String propertyName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return null;
+        }
+
+        PropertyInfo property = entity.getProperty( propertyName );
+        if ( property == null ) {
+            return null;
+        }
+
+        return property.getType();
+    }
+
+
+    /** @return value */
+    public boolean isPropertyIndexedInCollection( String containerType, String collectionName, String propertyName ) {
+
+        CollectionInfo collection = getCollection( containerType, collectionName );
+        if ( collection == null ) {
+            return false;
+        }
+
+        return collection.isPropertyIndexed( propertyName );
+    }
+
+
+    /** @return value */
+    public boolean hasDictionaries( String entityType ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        return entity.hasDictionaries();
+    }
+
+
+    /** @return value */
+    public Set<String> getDictionaryNames( String entityType ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return null;
+        }
+
+        return entity.getDictionaries().keySet();
+    }
+
+
+    /** @return value */
+    public boolean hasDictionary( String entityType, String dictionaryName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        return entity.hasDictionary( dictionaryName );
+    }
+
+
+    /** @return value */
+    public Class<?> getDictionaryKeyType( String entityType, String dictionaryName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return null;
+        }
+
+        DictionaryInfo set = entity.getDictionary( dictionaryName );
+        if ( set == null ) {
+            return null;
+        }
+
+        return set.getKeyType();
+    }
+
+
+    public Class<?> getDictionaryValueType( String entityType, String dictionaryName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return null;
+        }
+
+        DictionaryInfo dictionary = entity.getDictionary( dictionaryName );
+        if ( dictionary == null ) {
+            return null;
+        }
+
+        return dictionary.getValueType();
+    }
+
+
+    /** @return value */
+    public boolean isDictionaryIndexedInConnections( String entityType, String dictionaryName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        DictionaryInfo dictionary = entity.getDictionary( dictionaryName );
+        if ( dictionary == null ) {
+            return false;
+        }
+
+        return dictionary.isKeysIndexedInConnections();
+    }
+
+
+    /** @return value */
+    public boolean isDictionaryIndexedInCollection( String containerType, String collectionName,
+                                                    String dictionaryName ) {
+
+        CollectionInfo collection = getCollection( containerType, collectionName );
+        if ( collection == null ) {
+            return false;
+        }
+
+        return collection.isDictionaryIndexed( dictionaryName );
+    }
+
+
+    /** @return value */
+    public boolean hasCollection( String containerType, String collectionName ) {
+
+        return getCollection( containerType, collectionName ) != null;
+    }
+
+
+    public boolean isCollectionPathBased( String containerType, String collectionName ) {
+
+        CollectionInfo collection = getCollection( containerType, collectionName );
+        if ( collection == null ) {
+            return false;
+        }
+
+        EntityInfo item = getEntityInfo( collection.getType() );
+        if ( item == null ) {
+            return false;
+        }
+
+        PropertyInfo property = item.getAliasPropertyObject();
+        if ( property == null ) {
+            return false;
+        }
+
+        return property.isPathBasedName();
+    }
+
+
+    public boolean isCollectionReversed( String containerType, String collectionName ) {
+
+        CollectionInfo collection = getCollection( containerType, collectionName );
+        if ( collection == null ) {
+            return false;
+        }
+
+        return collection.isReversed();
+    }
+
+
+    public String getCollectionSort( String containerType, String collectionName ) {
+
+        CollectionInfo collection = getCollection( containerType, collectionName );
+        if ( collection == null ) {
+            return null;
+        }
+
+        return collection.getSort();
+    }
+
+
+    /** @return value */
+    public CollectionInfo getCollection( String containerType, String collectionName ) {
+
+        containerType = normalizeEntityType( containerType, true );
+
+        EntityInfo entity = getEntityInfo( containerType );
+        if ( entity == null ) {
+            return null;
+        }
+
+        CollectionInfo collection = entity.getCollection( collectionName );
+
+        if ( ( collection == null ) && ( Application.ENTITY_TYPE.equalsIgnoreCase( containerType ) ) ) {
+            collection = getDynamicApplicationCollection( collectionName );
+        }
+
+        return collection;
+    }
+
+
+    private CollectionInfo getDynamicApplicationCollection( String collectionName ) {
+        EntityInfo entity = getEntityInfo( Application.ENTITY_TYPE );
+        if ( entity == null ) {
+            return null;
+        }
+
+        CollectionInfo collection = entity.getCollection( collectionName );
+
+        if ( collection != null ) {
+            return collection;
+        }
+
+        collection = new CollectionInfo();
+        collection.setName( collectionName );
+        collection.setContainer( entity );
+        collection.setType( normalizeEntityType( collectionName ) );
+        Set<String> properties = new LinkedHashSet<String>();
+        properties.add( PROPERTY_NAME );
+        properties.add( PROPERTY_CREATED );
+        properties.add( PROPERTY_MODIFIED );
+        collection.setPropertiesIndexed( properties );
+        // entity.getCollections().put(collectionName, collection);
+        // mapCollector(collection.getType(), Application.ENTITY_TYPE,
+        // collectionName, collection);
+
+        return collection;
+    }
+
+
+    public String getCollectionType( String containerType, String collectionName ) {
+
+        containerType = normalizeEntityType( containerType );
+
+        CollectionInfo collection = getCollection( containerType, collectionName );
+
+        if ( collection == null ) {
+
+            if ( Application.ENTITY_TYPE.equalsIgnoreCase( containerType ) ) {
+                return normalizeEntityType( collectionName );
+            }
+            return null;
+        }
+
+        return collection.getType();
+    }
+
+
+    /** @return value */
+    public Map<String, CollectionInfo> getCollections( String entityType ) {
+
+        EntityInfo entity = getEntityInfo( normalizeEntityType( entityType, true ) );
+        if ( entity == null ) {
+            return null;
+        }
+
+        return entity.getCollections();
+    }
+
+
+    public Set<String> getCollectionNames( String entityType ) {
+
+        EntityInfo entity = getEntityInfo( normalizeEntityType( entityType, true ) );
+        if ( entity == null ) {
+            return null;
+        }
+
+        Map<String, CollectionInfo> map = entity.getCollections();
+
+        if ( map != null ) {
+            return map.keySet();
+        }
+
+        return null;
+    }
+
+
+    public java.util.List<String> getCollectionNamesAsList( String entityType ) {
+        Set<String> set = getCollectionNames( normalizeEntityType( entityType, true ) );
+        if ( set != null ) {
+            return new ArrayList<String>( set );
+        }
+        return null;
+    }
+
+
+    private Map<String, Set<CollectionInfo>> addDynamicApplicationCollectionAsContainer(
+            Map<String, Set<CollectionInfo>> containers, String entityType ) {
+
+        Map<String, Set<CollectionInfo>> copy =
+                new TreeMap<String, Set<CollectionInfo>>( String.CASE_INSENSITIVE_ORDER );
+        if ( containers != null ) {
+            copy.putAll( containers );
+        }
+        containers = copy;
+
+        if ( !containers.containsKey( Application.ENTITY_TYPE ) ) {
+            MapUtils.addMapSet( containers, true, Application.ENTITY_TYPE,
+                    getCollection( Application.ENTITY_TYPE, defaultCollectionName( entityType ) ) );
+        }
+
+        return containers;
+    }
+
+
+    /** @return value */
+    public Map<String, Set<CollectionInfo>> getContainers( String entityType ) {
+
+        entityType = normalizeEntityType( entityType );
+
+        // Add the application as a container to all entities
+        return addDynamicApplicationCollectionAsContainer( entityContainerCollections.get( entityType ), entityType );
+    }
+
+
+    /** @return value */
+    public CollectionInfo getContainerCollectionLinkedToCollection( String containerType, String collectionName ) {
+
+        CollectionInfo collection = getCollection( containerType, collectionName );
+        if ( collection == null ) {
+            return null;
+        }
+
+        String linkedCollection = collection.getLinkedCollection();
+        if ( linkedCollection == null ) {
+            return null;
+        }
+
+        return getCollection( collection.getType(), linkedCollection );
+    }
+
+
+    /** @return value */
+    public Map<String, Set<CollectionInfo>> getContainersIndexingProperties( String entityType ) {
+
+        entityType = normalizeEntityType( entityType );
+
+        // Add the application as a container indexing some properties by
+        // default
+        return addDynamicApplicationCollectionAsContainer(
+                entityContainerCollectionsIndexingProperties.get( entityType ), entityType );
+    }
+
+
+    /** @return value */
+    public Map<String, Set<CollectionInfo>> getContainersIndexingDictionaries( String entityType ) {
+
+        entityType = normalizeEntityType( entityType );
+
+        // Application does index any sets by default
+        return entityContainerCollectionsIndexingDictionaries.get( entityType );
+    }
+
+
+    /** @return value */
+    public Map<String, Set<CollectionInfo>> getContainersIndexingDynamicSetInfos( String entityType ) {
+
+        entityType = normalizeEntityType( entityType );
+
+        // Application does index dynamic sets by default
+        return entityContainerCollectionsIndexingDynamicDictionaries.get( entityType );
+    }
+
+
+    /** @return value */
+    public Map<String, Set<CollectionInfo>> getContainersIndexingProperty( String entityType, String propertyName ) {
+
+        entityType = normalizeEntityType( entityType );
+
+        Map<String, Map<String, Set<CollectionInfo>>> propertyContainerCollectionsIndexingPropertyInfo =
+                entityPropertyContainerCollectionsIndexingProperty.get( entityType );
+
+        // Application indexes name property by default
+        if ( propertyName.equalsIgnoreCase( PROPERTY_NAME ) || propertyName.equalsIgnoreCase( PROPERTY_CREATED )
+                || propertyName.equalsIgnoreCase( PROPERTY_MODIFIED ) ) {
+            return addDynamicApplicationCollectionAsContainer(
+                    propertyContainerCollectionsIndexingPropertyInfo != null ?
+                    propertyContainerCollectionsIndexingPropertyInfo.get( propertyName ) : null, entityType );
+        }
+
+        if ( propertyContainerCollectionsIndexingPropertyInfo == null ) {
+            return null;
+        }
+
+        return propertyContainerCollectionsIndexingPropertyInfo.get( propertyName );
+    }
+
+
+    /** @return value */
+    public Map<String, Set<CollectionInfo>> getContainersIndexingDictionary( String entityType,
+                                                                             String dictionaryName ) {
+
+        entityType = normalizeEntityType( entityType );
+
+        /*
+         * if (entityType == null) { return null; }
+         */
+
+        Map<String, Map<String, Set<CollectionInfo>>> dictionaryContainerCollectionsIndexingDictionary =
+                entityDictionaryContainerCollectionsIndexingDictionary.get( entityType );
+
+        if ( dictionaryContainerCollectionsIndexingDictionary == null ) {
+            return null;
+        }
+
+        // Application does index any set by default
+        return dictionaryContainerCollectionsIndexingDictionary.get( dictionaryName );
+    }
+
+
+    public static String defaultCollectionName( String entityType ) {
+        try {
+            return collectionNameCache.get( entityType );
+        }
+        catch ( ExecutionException ex ) {
+            ex.printStackTrace();
+        }
+        return _defaultCollectionName( entityType );
+    }
+
+
+    private static String _defaultCollectionName( String entityType ) {
+        entityType = normalizeEntityType( entityType );
+        return pluralize( entityType );
+    }
+
+
+    public static String normalizeEntityType( String entityType ) {
+        return normalizeEntityType( entityType, false );
+    }
+
+
+    public static String getAssociatedEntityType( String entityType ) {
+        if ( entityType == null ) {
+            return null;
+        }
+        entityType = stringOrSubstringAfterLast( entityType, ':' );
+        return normalizeEntityType( entityType, false );
+    }
+
+
+    public static String normalizeEntityType( String entityType, boolean baseType ) {
+        if ( entityType == null ) {
+            return null;
+        }
+        return baseType ? baseEntityTypes.getUnchecked( entityType ) : nonbaseEntityTypes.getUnchecked( entityType );
+    }
+
+
+    /** uncached - use normalizeEntityType() */
+    private static String createNormalizedEntityType( String entityType, boolean baseType ) {
+        if ( baseType ) {
+            int i = entityType.indexOf( ':' );
+            if ( i >= 0 ) {
+                entityType = entityType.substring( 0, i );
+            }
+        }
+        entityType = entityType.toLowerCase();
+        if ( entityType.startsWith( "org.apache.usergrid.persistence" ) ) {
+            entityType = stringOrSubstringAfterLast( entityType, '.' );
+        }
+        entityType = singularize( entityType );
+
+        if ( "dynamicentity".equalsIgnoreCase( entityType ) ) {
+            throw new IllegalArgumentException( entityType + " is not a valid entity type" );
+        }
+
+        // entityType = capitalizeDelimiter(entityType, '.', '_');
+        return entityType;
+    }
+
+
+    public static boolean isAssociatedEntityType( String entityType ) {
+        if ( entityType == null ) {
+            return false;
+        }
+        return entityType.indexOf( ':' ) != -1;
+    }
+
+
+    /** @return value */
+    public EntityInfo getDynamicEntityInfo( String entityType ) {
+
+        entityType = normalizeEntityType( entityType );
+
+        EntityInfo entity = new EntityInfo();
+        entity.setType( entityType );
+
+        Map<String, PropertyInfo> properties = new LinkedHashMap<String, PropertyInfo>();
+        PropertyInfo property = new PropertyInfo();
+        property.setName( PROPERTY_UUID );
+        property.setRequired( true );
+        property.setType( UUID.class );
+        property.setMutable( false );
+        property.setBasic( true );
+        properties.put( PROPERTY_UUID, property );
+
+        property = new PropertyInfo();
+        property.setName( PROPERTY_TYPE );
+        property.setRequired( true );
+        property.setType( String.class );
+        property.setMutable( false );
+        property.setBasic( true );
+        properties.put( PROPERTY_TYPE, property );
+
+        property = new PropertyInfo();
+        property.setName( PROPERTY_NAME );
+        property.setRequired( false );
+        property.setType( String.class );
+        property.setMutable( false );
+        property.setAliasProperty( true );
+        property.setIndexed( true );
+        property.setBasic( true );
+        property.setUnique( true );
+        properties.put( PROPERTY_NAME, property );
+
+        property = new PropertyInfo();
+        property.setName( PROPERTY_CREATED );
+        property.setRequired( true );
+        property.setType( Long.class );
+        property.setMutable( false );
+        property.setIndexed( true );
+        properties.put( PROPERTY_CREATED, property );
+
+        property = new PropertyInfo();
+        property.setName( PROPERTY_MODIFIED );
+        property.setRequired( true );
+        property.setType( Long.class );
+        property.setIndexed( true );
+        properties.put( PROPERTY_MODIFIED, property );
+
+        property = new PropertyInfo();
+        property.setName( PROPERTY_ITEM );
+        property.setRequired( false );
+        property.setType( UUID.class );
+        property.setMutable( false );
+        property.setAliasProperty( false );
+        property.setIndexed( false );
+        properties.put( PROPERTY_ITEM, property );
+
+        property = new PropertyInfo();
+        property.setName( PROPERTY_ITEM_TYPE );
+        property.setRequired( false );
+        property.setType( String.class );
+        property.setMutable( false );
+        property.setAliasProperty( false );
+        property.setIndexed( false );
+        properties.put( PROPERTY_ITEM_TYPE, property );
+
+        property = new PropertyInfo();
+        property.setName( PROPERTY_COLLECTION_NAME );
+        property.setRequired( false );
+        property.setType( String.class );
+        property.setMutable( false );
+        property.setAliasProperty( false );
+        property.setIndexed( false );
+        properties.put( PROPERTY_COLLECTION_NAME, property );
+
+        entity.setProperties( properties );
+
+        Map<String, DictionaryInfo> sets = new LinkedHashMap<String, DictionaryInfo>();
+
+        DictionaryInfo set = new DictionaryInfo();
+        set.setName( DICTIONARY_CONNECTIONS );
+        set.setKeyType( String.class );
+        sets.put( DICTIONARY_CONNECTIONS, set );
+
+        entity.setDictionaries( sets );
+
+        return entity;
+    }
+
+
+    public Map<String, Object> cleanUpdatedProperties( String entityType, Map<String, Object> properties ) {
+        return cleanUpdatedProperties( entityType, properties, false );
+    }
+
+
+    public Map<String, Object> cleanUpdatedProperties( String entityType, Map<String, Object> properties,
+                                                       boolean create ) {
+
+        if ( properties == null ) {
+            return null;
+        }
+
+        entityType = normalizeEntityType( entityType );
+
+        properties.remove( PROPERTY_UUID );
+        properties.remove( PROPERTY_TYPE );
+        properties.remove( PROPERTY_METADATA );
+        properties.remove( PROPERTY_MEMBERSHIP );
+        properties.remove( PROPERTY_CONNECTION );
+
+        Iterator<Entry<String, Object>> iterator = properties.entrySet().iterator();
+        while ( iterator.hasNext() ) {
+            Entry<String, Object> entry = iterator.next();
+            if ( hasProperty( entityType, entry.getKey() ) ) {
+                if ( !create && !isPropertyMutable( entityType, entry.getKey() ) ) {
+                    iterator.remove();
+                    continue;
+                }
+                Object propertyValue = entry.getValue();
+                if ( ( propertyValue instanceof String ) && ( ( ( String ) propertyValue ) == "" ) ) {
+                    propertyValue = null;
+                }
+                if ( ( propertyValue == null ) && isRequiredProperty( entityType, entry.getKey() ) ) {
+                    iterator.remove();
+                }
+            }
+        }
+        return properties;
+    }
+
+
+    public Object validateEntityPropertyValue( String entityType, String propertyName, Object propertyValue )
+            throws PropertyTypeConversionException {
+
+        entityType = normalizeEntityType( entityType );
+
+        if ( ( propertyValue instanceof String ) && ( ( String ) propertyValue ).equals( "" ) ) {
+            propertyValue = null;
+        }
+
+        if ( !hasProperty( entityType, propertyName ) ) {
+            return propertyValue;
+        }
+
+        /*
+         * if (PROPERTY_TYPE.equals(propertyName)) { return
+         * string(propertyValue); } else if (PROPERTY_ID.equals(propertyName)) {
+         * return uuid(propertyValue); }
+         */
+
+        Class<?> type = getPropertyType( entityType, propertyName );
+        if ( type != null ) {
+            // propertyValue = coerce(type, propertyValue);
+            try {
+                propertyValue = mapper.convertValue( propertyValue, type );
+            }
+            catch ( Exception e ) {
+                throw new PropertyTypeConversionException( entityType, propertyName, propertyValue, type, e );
+            }
+        }
+
+        return propertyValue;
+    }
+
+
+    public Object validateEntitySetValue( String entityType, String dictionaryName, Object elementValue ) {
+
+        entityType = normalizeEntityType( entityType );
+
+        if ( ( elementValue instanceof String ) && ( ( String ) elementValue ).equals( "" ) ) {
+            elementValue = null;
+        }
+
+        if ( !hasDictionary( entityType, dictionaryName ) ) {
+            return elementValue;
+        }
+
+        Class<?> type = getDictionaryKeyType( entityType, dictionaryName );
+        if ( type != null ) {
+            // elementValue = coerce(type, elementValue);
+            elementValue = mapper.convertValue( elementValue, type );
+        }
+
+        return elementValue;
+    }
+
+
+    public Entity toEntity( Map<String, Object> map ) {
+        Class<? extends Entity> entityClass = DynamicEntity.class;
+        String type = ( String ) map.get( PROPERTY_TYPE );
+        if ( type != null ) {
+            entityClass = getEntityClass( type );
+        }
+        if ( entityClass == null ) {
+            entityClass = DynamicEntity.class;
+        }
+        Entity entity = mapper.convertValue( map, entityClass );
+        return entity;
+    }
+
+    /*
+     * public Entity toEntity(Reader reader) { Entity entity =
+     * mapper.convertValue(reader, Entity.class); return entity; }
+     * 
+     * public Entity toEntity(InputStream input) { Entity entity =
+     * mapper.convertValue(input, Entity.class); return entity; }
+     * 
+     * public Entity toEntity(String string) { Entity entity =
+     * mapper.convertValue(string, Entity.class); return entity; }
+     */
+
+
+    public Map<String, Object> toMap( Entity entity ) {
+        Map<String, Object> map = mapper.convertValue( entity, new TypeReference<Map<String, Object>>() {} );
+        return map;
+    }
+
+
+    public Object convertToPropertyType( Class<? extends Entity> entityClass, String property, Object value ) {
+        Class<?> cls = getPropertyType( getEntityType( entityClass ), property );
+        if ( cls != null ) {
+            return mapper.convertValue( value, cls );
+        }
+        return value;
+    }
+
+
+    public Object convertToPropertyType( String type, String property, Object value ) {
+        Class<?> cls = getPropertyType( type, property );
+        if ( cls != null ) {
+            return mapper.convertValue( value, cls );
+        }
+        return value;
+    }
+
+
+    public PropertyDescriptor getDescriptorForEntityProperty( Class<? extends Entity> entityClass, String property ) {
+        Map<String, PropertyDescriptor> propertyDescriptors = entityClassPropertyToDescriptor.get( entityClass );
+        if ( propertyDescriptors == null ) {
+            return null;
+        }
+        return propertyDescriptors.get( property );
+    }
+
+
+    public void setEntityProperty( Entity entity, String property, Object value ) {
+        PropertyDescriptor descriptor = getDescriptorForEntityProperty( entity.getClass(), property );
+        if ( descriptor != null ) {
+            Class<?> cls = descriptor.getPropertyType();
+            if ( cls != null ) {
+                if ( ( value == null ) || ( cls.isAssignableFrom( value.getClass() ) ) ) {
+                    try {
+                        descriptor.getWriteMethod().invoke( entity, value );
+                        return;
+                    }
+                    catch ( Exception e ) {
+                        logger.error( "Unable to set entity property " + property, e );
+                    }
+                }
+                try {
+                    descriptor.getWriteMethod().invoke( entity, mapper.convertValue( value, cls ) );
+                    return;
+                }
+                catch ( Exception e ) {
+                    logger.error( "Unable to set entity property " + property, e );
+                }
+            }
+        }
+        entity.setDynamicProperty( property, value );
+    }
+
+
+    public Object getEntityProperty( Entity entity, String property ) {
+        PropertyDescriptor descriptor = getDescriptorForEntityProperty( entity.getClass(), property );
+        if ( descriptor != null ) {
+            try {
+                return descriptor.getReadMethod().invoke( entity );
+            }
+            catch ( Exception e ) {
+                logger.error( "Unable to get entity property " + property, e );
+            }
+            return null;
+        }
+        Map<String, Object> properties = entity.getDynamicProperties();
+        if ( properties != null ) {
+            return properties.get( property );
+        }
+        return null;
+    }
+
+
+    public Map<String, Object> getEntityProperties( Entity entity ) {
+        Map<String, Object> properties = new LinkedHashMap<String, Object>();
+        Map<String, PropertyDescriptor> propertyDescriptors = entityClassPropertyToDescriptor.get( entity.getClass() );
+
+        if ( propertyDescriptors == null ) {
+            registerEntity( entity.getClass() );
+            propertyDescriptors = entityClassPropertyToDescriptor.get( entity.getClass() );
+        }
+
+        for ( Entry<String, PropertyDescriptor> propertyEntry : propertyDescriptors.entrySet() ) {
+            String property = propertyEntry.getKey();
+            PropertyDescriptor descriptor = propertyEntry.getValue();
+            if ( descriptor != null ) {
+                try {
+                    Object value = descriptor.getReadMethod().invoke( entity );
+                    if ( value != null ) {
+                        properties.put( property, value );
+                    }
+                }
+                catch ( Exception e ) {
+                    logger.error( "Unable to get entity property " + property, e );
+                }
+            }
+        }
+        Map<String, Object> dynamicProperties = entity.getDynamicProperties();
+        if ( dynamicProperties != null ) {
+            properties.putAll( dynamicProperties );
+        }
+        return properties;
+    }
+
+
+    public static Map<String, Object> deserializeEntityProperties( Row<UUID, String, ByteBuffer> row ) {
+        if ( row == null ) {
+            return null;
+        }
+        ColumnSlice<String, ByteBuffer> slice = row.getColumnSlice();
+        if ( slice == null ) {
+            return null;
+        }
+        return deserializeEntityProperties( slice.getColumns(), true, false );
+    }
+
+
+    /** @return entity properties from columns as a map */
+    public static Map<String, Object> deserializeEntityProperties( List<HColumn<String, ByteBuffer>> columns ) {
+        return deserializeEntityProperties( CassandraPersistenceUtils.asMap( columns ), true, false );
+    }
+
+
+    public static Map<String, Object> deserializeEntityProperties( Map<String, ByteBuffer> columns ) {
+        return deserializeEntityProperties( columns, true, false );
+    }
+
+
+    public static Map<String, Object> deserializeEntityProperties( List<HColumn<String, ByteBuffer>> columns,
+                                                                   boolean checkId, boolean checkRequired ) {
+        return deserializeEntityProperties( CassandraPersistenceUtils.asMap( columns ), checkId, checkRequired );
+    }
+
+
+    /** @return entity properties from columns as a map */
+    public static Map<String, Object> deserializeEntityProperties( Map<String, ByteBuffer> columns, boolean checkId,
+                                                                   boolean checkRequired ) {
+
+        if ( columns == null ) {
+            return null;
+        }
+
+        String entityType = string( columns.get( PROPERTY_TYPE ) );
+        if ( entityType == null ) {
+            logger.debug( "deserializeEntityProperties(): No type for entity found, entity probably doesn't exist" );
+            return null;
+        }
+        if ( checkId && !columns.containsKey( PROPERTY_UUID ) ) {
+            logger.error( "No id for entity ( {} ) found!", entityType );
+            return null;
+        }
+
+        if ( checkRequired ) {
+            Set<String> required_properties = Schema.getDefaultSchema().getRequiredProperties( entityType );
+            if ( required_properties != null ) {
+                for ( String property_name : required_properties ) {
+                    if ( !columns.containsKey( property_name ) ) {
+                        logger.error( "Entity (" + entityType + ") missing required property: " + property_name,
+                                new Throwable() );
+                        return null;
+                    }
+                }
+            }
+        }
+
+        Map<String, Object> properties_map = new TreeMap<String, Object>( String.CASE_INSENSITIVE_ORDER );
+        for ( Entry<String, ByteBuffer> column : columns.entrySet() ) {
+            String propertyName = column.getKey();
+            Object propertyValue = deserializeEntityProperty( entityType, propertyName, column.getValue() );
+            properties_map.put( propertyName, propertyValue );
+        }
+        return properties_map;
+    }
+
+
+    /** @return object of correct type deserialize from column bytes */
+    public static Object deserializeEntityProperty( String entityType, String propertyName, ByteBuffer bytes ) {
+        Object propertyValue = null;
+        if ( PROPERTY_UUID.equals( propertyName ) ) {
+            propertyValue = uuid( bytes );
+        }
+        else if ( PROPERTY_TYPE.equals( propertyName ) ) {
+            propertyValue = string( bytes );
+        }
+        else {
+            if ( Schema.getDefaultSchema().isPropertyEncrypted( entityType, propertyName ) ) {
+                bytes = decrypt( bytes );
+            }
+            propertyValue = Schema.deserializePropertyValueFromJsonBinary( bytes );
+        }
+        return propertyValue;
+    }
+
+
+    public static ByteBuffer serializeEntityProperty( String entityType, String propertyName, Object propertyValue ) {
+        ByteBuffer bytes = null;
+        if ( PROPERTY_UUID.equals( propertyName ) ) {
+            bytes = bytebuffer( uuid( propertyValue ) );
+        }
+        else if ( PROPERTY_TYPE.equals( propertyName ) ) {
+            bytes = bytebuffer( string( propertyValue ) );
+        }
+        else {
+            bytes = Schema.serializePropertyValueToJsonBinary( toJsonNode( propertyValue ) );
+            if ( Schema.getDefaultSchema().isPropertyEncrypted( entityType, propertyName ) ) {
+                bytes.rewind();
+                bytes = encrypt( bytes );
+            }
+        }
+        return bytes;
+    }
+
+
+    public static ByteBuffer serializePropertyValueToJsonBinary( Object obj ) {
+        return JsonUtils.toByteBuffer( obj );
+    }
+
+
+    public static Object deserializePropertyValueFromJsonBinary( ByteBuffer bytes ) {
+        return JsonUtils.normalizeJsonTree( JsonUtils.fromByteBuffer( bytes ) );
+    }
+
+
+    public static Object deserializePropertyValueFromJsonBinary( ByteBuffer bytes, Class<?> classType ) {
+        return JsonUtils.normalizeJsonTree( JsonUtils.fromByteBuffer( bytes, classType ) );
+    }
+
+
+    public boolean isPropertyEncrypted( String entityType, String propertyName ) {
+
+        EntityInfo entity = getEntityInfo( entityType );
+        if ( entity == null ) {
+            return false;
+        }
+
+        PropertyInfo property = entity.getProperty( propertyName );
+        if ( property == null ) {
+            return false;
+        }
+
+        return property.isEncrypted();
+    }
+
+
+    private static final byte[] DEFAULT_ENCRYPTION_SEED =
+            "oWyWX?I2kZAhkKb_jQ8SZvjmgkiF4eGSjsfIkhnRetD4Dvtx2J".getBytes();
+    private static byte[] encryptionSeed =
+            ( System.getProperty( "encryptionSeed" ) != null ) ? System.getProperty( "encryptionSeed" ).getBytes() :
+            DEFAULT_ENCRYPTION_SEED;
+
+
+    public static ByteBuffer encrypt( ByteBuffer clear ) {
+        if ( clear == null || !clear.hasRemaining() ) {
+            return clear;
+        }
+        try {
+            SecretKeySpec sKeySpec = new SecretKeySpec( getRawKey( encryptionSeed ), "AES" );
+            Cipher cipher = Cipher.getInstance( "AES" );
+            cipher.init( Cipher.ENCRYPT_MODE, sKeySpec );
+            ByteBuffer encrypted = ByteBuffer.allocate( cipher.getOutputSize( clear.remaining() ) );
+            cipher.doFinal( clear, encrypted );
+            encrypted.rewind();
+            return encrypted;
+        }
+        catch ( Exception e ) {
+            throw new IllegalStateException( e );
+        }
+    }
+
+
+    public static ByteBuffer decrypt( ByteBuffer encrypted ) {
+        if ( encrypted == null || !encrypted.hasRemaining() ) {
+            return encrypted;
+        }
+        try {
+            SecretKeySpec sKeySpec = new SecretKeySpec( getRawKey( encryptionSeed ), "AES" );
+            Cipher cipher = Cipher.getInstance( "AES" );
+            cipher.init( Cipher.DECRYPT_MODE, sKeySpec );
+            ByteBuffer decrypted = ByteBuffer.allocate( cipher.getOutputSize( encrypted.remaining() ) );
+            cipher.doFinal( encrypted, decrypted );
+            decrypted.rewind();
+            return decrypted;
+        }
+        catch ( Exception e ) {
+            throw new IllegalStateException( e );
+        }
+    }
+
+
+    private static byte[] getRawKey( byte[] seed ) throws Exception {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance( "AES" );
+        SecureRandom sr = SecureRandom.getInstance( "SHA1PRNG" );
+        sr.setSeed( seed );
+        keyGenerator.init( 128, sr ); // 192 and 256 bits may not be available
+        SecretKey secretKey = keyGenerator.generateKey();
+        return secretKey.getEncoded();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/2c2acbe4/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleCollectionRef.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleCollectionRef.java b/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleCollectionRef.java
new file mode 100644
index 0000000..1d4742c
--- /dev/null
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleCollectionRef.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright 2012 Apigee Corporation
+ *
+ * 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.apache.usergrid.persistence;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.cassandra.CassandraPersistenceUtils;
+
+
+public class SimpleCollectionRef implements CollectionRef {
+
+    public static final String MEMBER_ENTITY_TYPE = "member";
+
+    protected final EntityRef ownerRef;
+    protected final String collectionName;
+    protected final EntityRef itemRef;
+    protected final String type;
+    protected final UUID id;
+
+
+    public SimpleCollectionRef( EntityRef ownerRef, String collectionName, EntityRef itemRef ) {
+        this.ownerRef = ownerRef;
+        this.collectionName = collectionName;
+        this.itemRef = itemRef;
+        type = itemRef.getType() + ":" + MEMBER_ENTITY_TYPE;
+        id = CassandraPersistenceUtils.keyID( ownerRef.getUuid(), collectionName, itemRef.getUuid() );
+    }
+
+
+    @Override
+    public EntityRef getOwnerEntity() {
+        return ownerRef;
+    }
+
+
+    @Override
+    public String getCollectionName() {
+        return collectionName;
+    }
+
+
+    @Override
+    public EntityRef getItemRef() {
+        return itemRef;
+    }
+
+
+    @Override
+    public UUID getUuid() {
+        return id;
+    }
+
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+
+    @Override
+    public String toString() {
+        if ( ( type == null ) && ( id == null ) ) {
+            return "CollectionRef(" + SimpleEntityRef.NULL_ID.toString() + ")";
+        }
+        if ( type == null ) {
+            return "CollectionRef(" + id.toString() + ")";
+        }
+        return type + "(" + id + "," + ownerRef + "," + collectionName + "," + itemRef + ")";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/2c2acbe4/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleEntityRef.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleEntityRef.java b/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleEntityRef.java
new file mode 100644
index 0000000..20d9d66
--- /dev/null
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleEntityRef.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright 2012 Apigee Corporation
+ *
+ * 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.apache.usergrid.persistence;
+
+
+import java.util.UUID;
+
+
+public class SimpleEntityRef implements EntityRef {
+
+    public static final UUID NULL_ID = new UUID( 0, 0 );
+
+    protected final String type;
+
+    protected final UUID id;
+
+
+    public SimpleEntityRef( UUID id ) {
+        this.id = id;
+        type = null;
+    }
+
+
+    public SimpleEntityRef( String type, UUID id ) {
+        this.type = type;
+        this.id = id;
+    }
+
+
+    public SimpleEntityRef( EntityRef entityRef ) {
+        type = entityRef.getType();
+        id = entityRef.getUuid();
+    }
+
+
+    public static EntityRef ref() {
+        return new SimpleEntityRef( null, null );
+    }
+
+
+    @Override
+    public UUID getUuid() {
+        return id;
+    }
+
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+
+    public static EntityRef ref( String entityType, UUID entityId ) {
+        return new SimpleEntityRef( entityType, entityId );
+    }
+
+
+    public static EntityRef ref( UUID entityId ) {
+        return new SimpleEntityRef( null, entityId );
+    }
+
+
+    public static EntityRef ref( EntityRef ref ) {
+        return new SimpleEntityRef( ref );
+    }
+
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ( ( id == null ) ? 0 : id.hashCode() );
+        result = prime * result + ( ( type == null ) ? 0 : type.hashCode() );
+        return result;
+    }
+
+
+    @Override
+    public boolean equals( Object obj ) {
+        if ( this == obj ) {
+            return true;
+        }
+        if ( obj == null ) {
+            return false;
+        }
+        if ( getClass() != obj.getClass() ) {
+            return false;
+        }
+        SimpleEntityRef other = ( SimpleEntityRef ) obj;
+        if ( id == null ) {
+            if ( other.id != null ) {
+                return false;
+            }
+        }
+        else if ( !id.equals( other.id ) ) {
+            return false;
+        }
+        if ( type == null ) {
+            if ( other.type != null ) {
+                return false;
+            }
+        }
+        else if ( !type.equals( other.type ) ) {
+            return false;
+        }
+        return true;
+    }
+
+
+    @Override
+    public String toString() {
+        if ( ( type == null ) && ( id == null ) ) {
+            return "EntityRef(" + NULL_ID.toString() + ")";
+        }
+        if ( type == null ) {
+            return "EntityRef(" + id.toString() + ")";
+        }
+        return type + "(" + id + ")";
+    }
+
+
+    public static UUID getUuid( EntityRef ref ) {
+        if ( ref == null ) {
+            return null;
+        }
+        return ref.getUuid();
+    }
+
+
+    public static String getType( EntityRef ref ) {
+        if ( ref == null ) {
+            return null;
+        }
+        return ref.getType();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/2c2acbe4/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleRoleRef.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleRoleRef.java b/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleRoleRef.java
new file mode 100644
index 0000000..0b07ea1
--- /dev/null
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/SimpleRoleRef.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright 2012 Apigee Corporation
+ *
+ * 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.apache.usergrid.persistence;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.cassandra.CassandraPersistenceUtils;
+import org.apache.usergrid.persistence.entities.Group;
+import org.apache.usergrid.utils.StringUtils;
+import org.apache.usergrid.utils.UUIDUtils;
+import org.springframework.util.Assert;
+
+
+public class SimpleRoleRef implements RoleRef {
+
+    protected final UUID groupId;
+    protected final String roleName;
+    protected final UUID id;
+
+
+    public SimpleRoleRef( String roleName ) {
+        this( null, roleName );
+    }
+
+
+    public SimpleRoleRef( UUID groupId, String roleName ) {
+        Assert.notNull( roleName );
+        if ( groupId != null ) {
+            this.groupId = groupId;
+        }
+        else {
+            this.groupId = UUIDUtils.tryExtractUUID( roleName );
+        }
+        this.roleName = StringUtils.stringOrSubstringAfterLast( roleName.toLowerCase(), ':' );
+        if ( groupId == null ) {
+            id = CassandraPersistenceUtils.keyID( "role", this.groupId, this.roleName );
+        }
+        else {
+            id = CassandraPersistenceUtils.keyID( "role", this.roleName );
+        }
+    }
+
+
+    public static SimpleRoleRef forRoleEntity( Entity role ) {
+        if ( role == null ) {
+            return null;
+        }
+        UUID groupId = ( UUID ) role.getProperty( "group" );
+        String name = role.getName();
+        return new SimpleRoleRef( groupId, name );
+    }
+
+
+    public static SimpleRoleRef forRoleName( String roleName ) {
+        return new SimpleRoleRef( null, roleName );
+    }
+
+
+    public static SimpleRoleRef forGroupIdAndRoleName( UUID groupId, String roleName ) {
+        return new SimpleRoleRef( groupId, roleName );
+    }
+
+
+    public static UUID getIdForRoleName( String roleName ) {
+        return forRoleName( roleName ).getUuid();
+    }
+
+
+    public static UUID getIdForGroupIdAndRoleName( UUID groupId, String roleName ) {
+        return forGroupIdAndRoleName( groupId, roleName ).getUuid();
+    }
+
+
+    @Override
+    public UUID getUuid() {
+        return id;
+    }
+
+
+    @Override
+    public String getType() {
+        return "role";
+    }
+
+
+    @Override
+    public EntityRef getGroupRef() {
+        return new SimpleEntityRef( Group.ENTITY_TYPE, groupId );
+    }
+
+
+    @Override
+    public String getRoleName() {
+        return roleName;
+    }
+
+
+    @Override
+    public UUID getGroupId() {
+        return groupId;
+    }
+
+
+    @Override
+    public String getApplicationRoleName() {
+        if ( groupId == null ) {
+            return roleName;
+        }
+        return groupId + ":" + roleName;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/2c2acbe4/stack/core/src/main/java/org/apache/usergrid/persistence/TypedEntity.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/TypedEntity.java b/stack/core/src/main/java/org/apache/usergrid/persistence/TypedEntity.java
new file mode 100644
index 0000000..36e38fe
--- /dev/null
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/TypedEntity.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright 2012 Apigee Corporation
+ *
+ * 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.apache.usergrid.persistence;
+
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+/**
+ * TypedEntity is the abstract superclass for all typed entities. A typed entity refers to an entity that has a concrete
+ * Java class mapped to it. Entities do not need to have concrete typed classes, the service interacts with entities in
+ * an entirely dynamic fashion and uses the Schema class to determine relationships and property types, however using
+ * the typed entity classes can be more convenient.
+ *
+ * @author edanuff
+ */
+@XmlRootElement
+public abstract class TypedEntity extends AbstractEntity {
+
+    @Override
+    public Entity toTypedEntity() {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/2c2acbe4/stack/core/src/main/java/org/apache/usergrid/persistence/Update.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/Update.java b/stack/core/src/main/java/org/apache/usergrid/persistence/Update.java
new file mode 100644
index 0000000..013d490
--- /dev/null
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/Update.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright 2012 Apigee Corporation
+ *
+ * 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.apache.usergrid.persistence;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class Update {
+
+    private List<UpdateOperation> operationList = new ArrayList<UpdateOperation>();
+
+
+    public class UpdateOperation {
+        String propertyName;
+        UpdateOperator operator;
+        Object value;
+
+
+        UpdateOperation( String propertyName, UpdateOperator operator, Object value ) {
+            this.propertyName = propertyName;
+            this.operator = operator;
+            this.value = value;
+        }
+
+
+        public String getPropertyName() {
+            return propertyName;
+        }
+
+
+        public UpdateOperator getOperator() {
+            return operator;
+        }
+
+
+        public Object getValue() {
+            return value;
+        }
+    }
+
+
+    public static enum UpdateOperator {
+        UPDATE, DELETE, ADD_TO_LIST, REMOVE_FROM_LIST;
+    }
+
+
+    public Update() {
+    }
+
+
+    public void add( String propertyName, UpdateOperator operator, Object value ) {
+        UpdateOperation operation = new UpdateOperation( propertyName, operator, value );
+        operationList.add( operation );
+    }
+
+
+    public void clear() {
+        operationList = new ArrayList<UpdateOperation>();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/2c2acbe4/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityCollection.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityCollection.java b/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityCollection.java
new file mode 100644
index 0000000..332782b
--- /dev/null
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityCollection.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright 2012 Apigee Corporation
+ *
+ * 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.apache.usergrid.persistence.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Target(value = { ElementType.METHOD, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EntityCollection {
+    String type();
+
+    String[] dictionariesIndexed() default { };
+
+    boolean indexingDynamicDictionaries() default false;
+
+    String linkedCollection() default "";
+
+    boolean publicVisible() default true;
+
+    boolean reversed() default false;
+
+    boolean includedInExport() default true;
+
+    String sort() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/2c2acbe4/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityDictionary.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityDictionary.java b/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityDictionary.java
new file mode 100644
index 0000000..539f8d4
--- /dev/null
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityDictionary.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright 2012 Apigee Corporation
+ *
+ * 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.apache.usergrid.persistence.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Target(value = { ElementType.METHOD, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EntityDictionary {
+
+    Class<?> keyType();
+
+    boolean keysIndexedInConnections() default false;
+
+    Class<?> valueType() default Object.class;
+
+    boolean publicVisible() default true;
+
+    boolean includedInExport() default true;
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/2c2acbe4/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityProperty.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityProperty.java b/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityProperty.java
new file mode 100644
index 0000000..6adee94
--- /dev/null
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/annotations/EntityProperty.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright 2012 Apigee Corporation
+ *
+ * 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.apache.usergrid.persistence.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Target(value = { ElementType.METHOD, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EntityProperty {
+
+    String name() default "";
+
+    boolean indexed() default true;
+
+    boolean basic() default false;
+
+    boolean required() default false;
+
+    boolean mutable() default true;
+
+    boolean unique() default false;
+
+    boolean aliasProperty() default false;
+
+    boolean pathBasedName() default false;
+
+    boolean fulltextIndexed() default false;
+
+    boolean publicVisible() default false;
+
+    boolean includedInExport() default true;
+
+    boolean timestamp() default false;
+
+    boolean encrypted() default false;
+}