blob: f44fffa49b1dc7a29ac77685df703bb1fb0f277e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.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.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.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.smile.SmileFactory;
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_ADMIN = "admin";
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 final List<String> entitiesPackage = new ArrayList<String>();
private static final 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( SerializationFeature.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() {
Set<String> strings = allProperties.keySet();
return strings.toArray(new String[strings.size()]);
}
/** @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.fieldNames();
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 );
return entity != null && 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];
}
Set<String> strings = entity.getProperties().keySet();
return strings.toArray(new String[strings.size()]);
}
/** @return value */
public boolean hasProperty( String entityType, String propertyName ) {
if ( propertyName.equals( PROPERTY_UUID ) || propertyName.equals( PROPERTY_TYPE ) ) {
return true;
}
EntityInfo entity = getEntityInfo( entityType );
return entity != null && 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 );
return entity != null && entity.isPropertyMutable(propertyName);
}
public boolean isPropertyUnique( String entityType, String propertyName ) {
EntityInfo entity = getEntityInfo( entityType );
return entity != null && entity.isPropertyUnique(propertyName);
}
public boolean isPropertyIndexed( String entityType, String propertyName ) {
EntityInfo entity = getEntityInfo( entityType );
return entity == null || !entity.hasProperty(propertyName) || entity.isPropertyIndexed(propertyName);
}
public boolean isPropertyFulltextIndexed( String entityType, String propertyName ) {
EntityInfo entity = getEntityInfo( entityType );
return entity == null || !entity.hasProperty(propertyName) || entity.isPropertyFulltextIndexed(propertyName);
}
public boolean isPropertyTimestamp( String entityType, String propertyName ) {
EntityInfo entity = getEntityInfo( entityType );
return entity != null && 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 );
return entity != null && 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 );
return collection != null && collection.isPropertyIndexed(propertyName);
}
/** @return value */
public boolean hasDictionaries( String entityType ) {
EntityInfo entity = getEntityInfo( entityType );
return entity != null && 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 );
return entity != null && 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 );
return dictionary != null && dictionary.isKeysIndexedInConnections();
}
/** @return value */
public boolean isDictionaryIndexedInCollection( String containerType, String collectionName,
String dictionaryName ) {
CollectionInfo collection = getCollection( containerType, collectionName );
return collection != null && 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();
return property != null && property.isPathBasedName();
}
public boolean isCollectionReversed( String containerType, String collectionName ) {
CollectionInfo collection = getCollection( containerType, collectionName );
return collection != null && 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 ) {
return entityType != null && entityType.contains(":");
}
/** @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 ) && (propertyValue.equals("")) ) {
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 ) && 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 ) && 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;
}
return mapper.convertValue( map, entityClass );
}
/*
* 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 ) {
return mapper.convertValue( entity, new TypeReference<Map<String, Object>>() {} );
}
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 );
return property != null && 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();
}
}