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;
+}