org.usergrid.persistence.cassandra.EntityManagerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of usergrid-core Show documentation
Show all versions of usergrid-core Show documentation
Core services for Usergrid system.
/*******************************************************************************
* 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.usergrid.persistence.cassandra;
import static java.lang.String.CASE_INSENSITIVE_ORDER;
import static java.util.Arrays.asList;
import static me.prettyprint.hector.api.factory.HFactory.createCounterSliceQuery;
import static me.prettyprint.hector.api.factory.HFactory.createIndexedSlicesQuery;
import static me.prettyprint.hector.api.factory.HFactory.createMutator;
import static org.apache.commons.lang.StringUtils.capitalize;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.usergrid.locking.LockHelper.getUniqueUpdateLock;
import static org.usergrid.persistence.Results.fromEntities;
import static org.usergrid.persistence.Results.Level.REFS;
import static org.usergrid.persistence.Schema.COLLECTION_ROLES;
import static org.usergrid.persistence.Schema.COLLECTION_USERS;
import static org.usergrid.persistence.Schema.DICTIONARY_COLLECTIONS;
import static org.usergrid.persistence.Schema.DICTIONARY_PERMISSIONS;
import static org.usergrid.persistence.Schema.DICTIONARY_PROPERTIES;
import static org.usergrid.persistence.Schema.DICTIONARY_ROLENAMES;
import static org.usergrid.persistence.Schema.DICTIONARY_ROLETIMES;
import static org.usergrid.persistence.Schema.DICTIONARY_SETS;
import static org.usergrid.persistence.Schema.PROPERTY_ASSOCIATED;
import static org.usergrid.persistence.Schema.PROPERTY_CREATED;
import static org.usergrid.persistence.Schema.PROPERTY_INACTIVITY;
import static org.usergrid.persistence.Schema.PROPERTY_MODIFIED;
import static org.usergrid.persistence.Schema.PROPERTY_NAME;
import static org.usergrid.persistence.Schema.PROPERTY_TIMESTAMP;
import static org.usergrid.persistence.Schema.PROPERTY_TYPE;
import static org.usergrid.persistence.Schema.PROPERTY_UUID;
import static org.usergrid.persistence.Schema.TYPE_APPLICATION;
import static org.usergrid.persistence.Schema.TYPE_CONNECTION;
import static org.usergrid.persistence.Schema.TYPE_ENTITY;
import static org.usergrid.persistence.Schema.TYPE_MEMBER;
import static org.usergrid.persistence.Schema.TYPE_ROLE;
import static org.usergrid.persistence.Schema.defaultCollectionName;
import static org.usergrid.persistence.Schema.deserializeEntityProperties;
import static org.usergrid.persistence.Schema.getDefaultSchema;
import static org.usergrid.persistence.SimpleEntityRef.getUuid;
import static org.usergrid.persistence.SimpleEntityRef.ref;
import static org.usergrid.persistence.SimpleRoleRef.getIdForGroupIdAndRoleName;
import static org.usergrid.persistence.SimpleRoleRef.getIdForRoleName;
import static org.usergrid.persistence.cassandra.ApplicationCF.APPLICATION_AGGREGATE_COUNTERS;
import static org.usergrid.persistence.cassandra.ApplicationCF.ENTITY_ALIASES;
import static org.usergrid.persistence.cassandra.ApplicationCF.ENTITY_COMPOSITE_DICTIONARIES;
import static org.usergrid.persistence.cassandra.ApplicationCF.ENTITY_COUNTERS;
import static org.usergrid.persistence.cassandra.ApplicationCF.ENTITY_DICTIONARIES;
import static org.usergrid.persistence.cassandra.ApplicationCF.ENTITY_ID_SETS;
import static org.usergrid.persistence.cassandra.ApplicationCF.ENTITY_PROPERTIES;
import static org.usergrid.persistence.cassandra.ApplicationCF.ENTITY_UNIQUE;
import static org.usergrid.persistence.cassandra.CassandraPersistenceUtils.addDeleteToMutator;
import static org.usergrid.persistence.cassandra.CassandraPersistenceUtils.addInsertToMutator;
import static org.usergrid.persistence.cassandra.CassandraPersistenceUtils.addPropertyToMutator;
import static org.usergrid.persistence.cassandra.CassandraPersistenceUtils.batchExecute;
import static org.usergrid.persistence.cassandra.CassandraPersistenceUtils.key;
import static org.usergrid.persistence.cassandra.CassandraPersistenceUtils.toStorableBinaryValue;
import static org.usergrid.persistence.cassandra.CassandraService.ALL_COUNT;
import static org.usergrid.utils.ClassUtils.cast;
import static org.usergrid.utils.ConversionUtils.bytebuffer;
import static org.usergrid.utils.ConversionUtils.bytes;
import static org.usergrid.utils.ConversionUtils.getLong;
import static org.usergrid.utils.ConversionUtils.object;
import static org.usergrid.utils.ConversionUtils.string;
import static org.usergrid.utils.ConversionUtils.uuid;
import static org.usergrid.utils.InflectionUtils.singularize;
import static org.usergrid.utils.UUIDUtils.getTimestampInMicros;
import static org.usergrid.utils.UUIDUtils.getTimestampInMillis;
import static org.usergrid.utils.UUIDUtils.isTimeBased;
import static org.usergrid.utils.UUIDUtils.newTimeUUID;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import me.prettyprint.cassandra.model.IndexedSlicesQuery;
import me.prettyprint.cassandra.serializers.ByteBufferSerializer;
import me.prettyprint.cassandra.serializers.LongSerializer;
import me.prettyprint.cassandra.serializers.StringSerializer;
import me.prettyprint.cassandra.serializers.UUIDSerializer;
import me.prettyprint.hector.api.Keyspace;
import me.prettyprint.hector.api.beans.ColumnSlice;
import me.prettyprint.hector.api.beans.CounterRow;
import me.prettyprint.hector.api.beans.CounterRows;
import me.prettyprint.hector.api.beans.CounterSlice;
import me.prettyprint.hector.api.beans.DynamicComposite;
import me.prettyprint.hector.api.beans.HColumn;
import me.prettyprint.hector.api.beans.HCounterColumn;
import me.prettyprint.hector.api.beans.OrderedRows;
import me.prettyprint.hector.api.beans.Row;
import me.prettyprint.hector.api.beans.Rows;
import me.prettyprint.hector.api.factory.HFactory;
import me.prettyprint.hector.api.mutation.Mutator;
import me.prettyprint.hector.api.query.MultigetSliceCounterQuery;
import me.prettyprint.hector.api.query.QueryResult;
import me.prettyprint.hector.api.query.SliceCounterQuery;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.usergrid.locking.Lock;
import org.usergrid.mq.Message;
import org.usergrid.mq.QueueManager;
import org.usergrid.mq.cassandra.QueueManagerFactoryImpl;
import org.usergrid.persistence.AggregateCounter;
import org.usergrid.persistence.AggregateCounterSet;
import org.usergrid.persistence.AssociatedEntityRef;
import org.usergrid.persistence.CollectionRef;
import org.usergrid.persistence.ConnectedEntityRef;
import org.usergrid.persistence.ConnectionRef;
import org.usergrid.persistence.CounterResolution;
import org.usergrid.persistence.DynamicEntity;
import org.usergrid.persistence.Entity;
import org.usergrid.persistence.EntityFactory;
import org.usergrid.persistence.EntityManager;
import org.usergrid.persistence.EntityRef;
import org.usergrid.persistence.Identifier;
import org.usergrid.persistence.IndexBucketLocator;
import org.usergrid.persistence.IndexBucketLocator.IndexType;
import org.usergrid.persistence.Query;
import org.usergrid.persistence.Query.CounterFilterPredicate;
import org.usergrid.persistence.Results;
import org.usergrid.persistence.Results.Level;
import org.usergrid.persistence.RoleRef;
import org.usergrid.persistence.Schema;
import org.usergrid.persistence.SimpleCollectionRef;
import org.usergrid.persistence.SimpleEntityRef;
import org.usergrid.persistence.SimpleRoleRef;
import org.usergrid.persistence.TypedEntity;
import org.usergrid.persistence.cassandra.CounterUtils.AggregateCounterSelection;
import org.usergrid.persistence.cassandra.util.TraceParticipant;
import org.usergrid.persistence.entities.Application;
import org.usergrid.persistence.entities.Event;
import org.usergrid.persistence.entities.Group;
import org.usergrid.persistence.entities.Role;
import org.usergrid.persistence.entities.User;
import org.usergrid.persistence.exceptions.DuplicateUniquePropertyExistsException;
import org.usergrid.persistence.exceptions.EntityNotFoundException;
import org.usergrid.persistence.exceptions.RequiredPropertyNotFoundException;
import org.usergrid.persistence.exceptions.UnexpectedEntityTypeException;
import org.usergrid.persistence.schema.CollectionInfo;
import org.usergrid.utils.ClassUtils;
import org.usergrid.utils.CompositeUtils;
import org.usergrid.utils.UUIDUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.yammer.metrics.annotation.Metered;
import javax.annotation.Resource;
/**
* Cassandra-specific implementation of Datastore
*
* @author edanuff
*
*/
public class EntityManagerImpl implements EntityManager {
/** The log4j logger. */
private static final Logger logger = LoggerFactory
.getLogger(EntityManagerImpl.class);
public static final String APPLICATION_COLLECTION = "application.collection.";
public static final String APPLICATION_ENTITIES = "application.entities";
public static final long ONE_COUNT = 1L;
@Resource
private EntityManagerFactoryImpl emf;
@Resource
private QueueManagerFactoryImpl qmf;
@Resource
private IndexBucketLocator indexBucketLocator;
private UUID applicationId;
private Application application;
@Resource
private CassandraService cass;
@Resource
private CounterUtils counterUtils;
private boolean skipAggregateCounters;
public static final StringSerializer se = new StringSerializer();
public static final ByteBufferSerializer be = new ByteBufferSerializer();
public static final UUIDSerializer ue = new UUIDSerializer();
public static final LongSerializer le = new LongSerializer();
public EntityManagerImpl() {
}
public EntityManagerImpl init(EntityManagerFactoryImpl emf,
CassandraService cass, CounterUtils counterUtils, UUID applicationId,
boolean skipAggregateCounters) {
this.emf = emf;
this.cass = cass;
this.counterUtils = counterUtils;
this.applicationId = applicationId;
this.skipAggregateCounters = skipAggregateCounters;
qmf = (QueueManagerFactoryImpl) getApplicationContext().getBean("queueManagerFactory");
indexBucketLocator = (IndexBucketLocator) getApplicationContext().getBean("indexBucketLocator");
// prime the application entity for the EM
try {
getApplication();
} catch (Exception ex) {
ex.printStackTrace();
}
return this;
}
public void setApplicationId(UUID applicationId) {
this.applicationId = applicationId;
}
public ApplicationContext getApplicationContext() {
return emf.applicationContext;
}
@Override
public EntityRef getApplicationRef() {
return ref(TYPE_APPLICATION, applicationId);
}
@Override
public Application getApplication() throws Exception {
if ( application == null) {
application = get(applicationId, Application.class);
}
return application;
}
@Override
public void updateApplication(Application app) throws Exception {
update(app);
this.application = app;
}
@Override
public void updateApplication(Map properties)
throws Exception {
this.updateProperties(applicationId, properties);
this.application = get(applicationId, Application.class);
}
@Override
public RelationManagerImpl getRelationManager(EntityRef entityRef) {
//RelationManagerImpl rmi = applicationContext.getBean(RelationManagerImpl.class);
RelationManagerImpl rmi = new RelationManagerImpl();
rmi.init(this,cass,applicationId,entityRef,indexBucketLocator);
return rmi;
}
/**
* Batch dictionary property.
*
* @param batch
* the batch
* @param applicationId
* the application id
* @param entityType
* the entity type
* @param entityId
* the entity id
* @param properties
* the properties
* @param propertyName
* the property name
* @param propertyValue
* the property value
* @param timestamp
* the timestamp
* @return batch
* @throws Exception
* the exception
*/
public Mutator batchSetProperty(Mutator batch,
Entity entity, String propertyName, Object propertyValue,
UUID timestampUuid) throws Exception {
return this.batchSetProperty(batch, entity, propertyName,
propertyValue, false, false, timestampUuid);
}
public Mutator batchSetProperty(Mutator batch,
Entity entity, String propertyName, Object propertyValue,
boolean force, boolean noRead, UUID timestampUuid) throws Exception {
long timestamp = getTimestampInMicros(timestampUuid);
// propertyName = propertyName.toLowerCase();
boolean entitySchemaHasProperty = getDefaultSchema().hasProperty(
entity.getType(), propertyName);
propertyValue = getDefaultSchema().validateEntityPropertyValue(
entity.getType(), propertyName, propertyValue);
Schema defaultSchema = Schema.getDefaultSchema();
if (PROPERTY_TYPE.equalsIgnoreCase(propertyName)
&& (propertyValue != null)) {
if ("entity".equalsIgnoreCase(propertyValue.toString())
|| "dynamicentity".equalsIgnoreCase(propertyValue
.toString())) {
String errorMsg = "Unable to dictionary entity type to "
+ propertyValue + " because that is not a valid type.";
logger.error(errorMsg);
throw new IllegalArgumentException(errorMsg);
}
}
if (entitySchemaHasProperty) {
if (!force) {
if (!defaultSchema.isPropertyMutable(entity.getType(),
propertyName)) {
return batch;
}
// Passing null for propertyValue indicates delete the property
// so if required property, exit
if ((propertyValue == null)
&& defaultSchema.isRequiredProperty(
entity.getType(), propertyName)) {
return batch;
}
}
if (!isPropertyValueUniqueForEntity(entity.getUuid(),
entity.getType(), propertyName, propertyValue)) {
throw new DuplicateUniquePropertyExistsException(
entity.getType(), propertyName, propertyValue);
}
if (propertyName.equals(defaultSchema.aliasProperty(
entity.getType()))) {
Lock lock = getUniqueUpdateLock(cass.getLockManager(), applicationId, propertyValue, entity.getType(), propertyName);
try{
lock.lock();
deleteAliasesForEntity(batch, entity.getUuid(), timestamp-1);
createAlias(batch, applicationId, entity, entity.getType(),
string(propertyValue), timestamp);
}finally{
lock.unlock();
}
}
/**
* Unique property, load the old value and remove it, check if it's not a duplicate
*/
if(defaultSchema.getEntityInfo(entity.getType()).isPropertyUnique(propertyName)){
Lock lock = getUniqueUpdateLock(cass.getLockManager(),applicationId, propertyValue,
entity.getType(), propertyName);
try {
lock.lock();
String collectionName = Schema.defaultCollectionName(entity.getType());
uniquePropertyDelete(batch, collectionName, entity.getType(), propertyName, propertyValue, entity.getUuid(), timestamp-1);
uniquePropertyWrite(batch, collectionName, propertyName, propertyValue, entity.getUuid(), timestamp);
} finally {
lock.unlock();
}
}
}
if (getDefaultSchema()
.isPropertyIndexed(entity.getType(), propertyName)) {
getRelationManager(entity).batchUpdatePropertyIndexes(batch,
propertyName, propertyValue, entitySchemaHasProperty,
noRead, timestampUuid);
}
if (propertyValue != null) {
// Set the new value
addPropertyToMutator(batch, key(entity.getUuid()),
entity.getType(), propertyName, propertyValue, timestamp);
if (!entitySchemaHasProperty) {
// Make a list of all the properties ever dictionary on this
// entity
addInsertToMutator(batch, ENTITY_DICTIONARIES,
key(entity.getUuid(), DICTIONARY_PROPERTIES),
propertyName, null, timestamp);
}
} else {
addDeleteToMutator(batch, ENTITY_PROPERTIES, key(entity.getUuid()),
propertyName, timestamp);
}
return batch;
}
/**
* Batch update properties.
*
* @param batch
* the batch
* @param applicationId
* the application id
* @param entityType
* the entity type
* @param entityId
* the entity id
* @param entityProperties
* the entity properties
* @param properties
* the properties
* @param timestamp
* the timestamp
* @return batch
* @throws Exception
* the exception
*/
public Mutator batchUpdateProperties(Mutator batch,
Entity entity, Map properties, UUID timestampUuid)
throws Exception {
for (String propertyName : properties.keySet()) {
Object propertyValue = properties.get(propertyName);
batch = batchSetProperty(batch, entity, propertyName,
propertyValue, timestampUuid);
}
return batch;
}
/**
* Batch update properties.
*
* @param batch
* the batch
* @param applicationId
* the application id
* @param entityId
* the entity id
* @param properties
* the properties
* @param timestamp
* the timestamp
* @return batch
* @throws Exception
* the exception
*/
public Mutator batchUpdateProperties(Mutator batch,
UUID entityId, Map properties, UUID timestampUuid)
throws Exception {
DynamicEntity entity = loadPartialEntity(entityId);
if (entity == null) {
return batch;
}
for (String propertyName : properties.keySet()) {
Object propertyValue = properties.get(propertyName);
batch = batchSetProperty(batch, entity, propertyName,
propertyValue, timestampUuid);
}
return batch;
}
/**
* Batch update set.
*
* @param batch
* the batch
* @param applicationId
* the application id
* @param entityType
* the entity type
* @param entityId
* the entity id
* @param properties
* the properties
* @param dictionaryName
* the dictionary name
* @param property
* the property
* @param elementValue
* the dictionary value
* @param removeFromSet
* the remove from set
* @param timestamp
* the timestamp
* @return batch
* @throws Exception
* the exception
*/
public Mutator batchUpdateDictionary(Mutator batch,
Entity entity, String dictionaryName, Object elementValue,
boolean removeFromDictionary, UUID timestampUuid) throws Exception {
return batchUpdateDictionary(batch, entity, dictionaryName,
elementValue, null, removeFromDictionary, timestampUuid);
}
public Mutator batchUpdateDictionary(Mutator batch,
Entity entity, String dictionaryName, Object elementValue,
Object elementCoValue, boolean removeFromDictionary,
UUID timestampUuid) throws Exception {
long timestamp = getTimestampInMicros(timestampUuid);
// dictionaryName = dictionaryName.toLowerCase();
if (elementCoValue == null) {
elementCoValue = ByteBuffer.allocate(0);
}
boolean entityHasDictionary = getDefaultSchema().hasDictionary(
entity.getType(), dictionaryName);
// Don't index dynamic dictionaries not defined by the schema
if (entityHasDictionary) {
getRelationManager(entity).batchUpdateSetIndexes(batch,
dictionaryName, elementValue, removeFromDictionary,
timestampUuid);
}
ApplicationCF dictionary_cf = entityHasDictionary ? ENTITY_DICTIONARIES
: ENTITY_COMPOSITE_DICTIONARIES;
if (elementValue != null) {
if (!removeFromDictionary) {
// Set the new value
elementCoValue = toStorableBinaryValue(elementCoValue,
!entityHasDictionary);
addInsertToMutator(batch, dictionary_cf,
key(entity.getUuid(), dictionaryName),
entityHasDictionary ? elementValue
: asList(elementValue), elementCoValue,
timestamp);
if (!entityHasDictionary) {
addInsertToMutator(batch, ENTITY_DICTIONARIES,
key(entity.getUuid(), DICTIONARY_SETS),
dictionaryName, null, timestamp);
}
} else {
addDeleteToMutator(batch, dictionary_cf,
key(entity.getUuid(), dictionaryName),
entityHasDictionary ? elementValue
: asList(elementValue), timestamp);
}
}
return batch;
}
/**
* Returns true if the property is unique, and the entity can be saved. If it's not unique, false is returned
* @param thisEntity
* @param entityType
* @param propertyName
* @param propertyValue
* @return
* @throws Exception
*/
@Metered(group="core", name="EntityManager_isPropertyValueUniqueForEntity")
public boolean isPropertyValueUniqueForEntity(UUID thisEntity,
String entityType, String propertyName, Object propertyValue)
throws Exception {
if (!getDefaultSchema().isPropertyUnique(entityType, propertyName)) {
return true;
}
if (propertyValue == null) {
return true;
}
String collectionName = defaultCollectionName(entityType);
Lock lock = getUniqueUpdateLock(cass.getLockManager(), applicationId, propertyValue, entityType, propertyName);
List> cols = null;
try {
lock.lock();
Object key = createUniqueIndexKey(collectionName, propertyName, propertyValue);
cols = cass.getColumns(cass.getApplicationKeyspace(applicationId),
ENTITY_UNIQUE, key, null, null, 2, false);
} finally {
lock.unlock();
}
//No columns at all, it's unique
if(cols.size() == 0){
return true;
}
//shouldn't happen, but it's an error case
if(cols.size() > 1){
logger.error("INDEX CORRUPTION: More than 1 unique value exists for entities in application {} of type {} on property {} with value {}", new Object[]{applicationId, entityType, propertyName, propertyValue});
}
/**
* Doing this in a loop sucks, but we need to account for possibly having more than 1 entry in the index due to corruption. We need to allow them to update, otherwise
* both entities will be unable to update and must be deleted
*/
for(HColumn col: cols){
UUID id = ue.fromByteBuffer(col.getName());
if(thisEntity.equals(id)){
return true;
}
}
return false;
}
/**
* Add this unique index to the delete
* @param m
* @param collectionName
* @param propertyName
* @param propertyValue
* @param entityId
* @param timestamp
* @throws Exception
*/
private void uniquePropertyDelete(Mutator m, String collectionName, String entityType, String propertyName, Object propertyValue, UUID entityId, long timestamp) throws Exception{
//read the old value and delete it
Object oldValue = getProperty(new SimpleEntityRef(entityType, entityId), propertyName);
//we have an old value. If the new value is empty, we want to delete the old value. If the new value is different we want to delete, otherwise we don't issue the delete
if(oldValue != null && (propertyValue == null || !oldValue.equals(propertyValue))){
Object key = createUniqueIndexKey(collectionName, propertyName, oldValue);
addDeleteToMutator(m, ENTITY_UNIQUE, key, timestamp, entityId);
}
}
/**
* Add this unique index to the delete
* @param m
* @param collectionName
* @param propertyName
* @param propertyValue
* @param entityId
* @param timestamp
* @throws Exception
*/
private void uniquePropertyWrite(Mutator m, String collectionName, String propertyName, Object propertyValue, UUID entityId, long timestamp) throws Exception{
Object key = createUniqueIndexKey(collectionName, propertyName, propertyValue);
addInsertToMutator(m, ENTITY_UNIQUE, key, entityId, null, timestamp);
}
/**
* Create a row key for the entity of the given type with the name and value in the property. Used for fast unique index lookups
* @param collectionName
* @param propertyName
* @param value
* @return
*/
private Object createUniqueIndexKey(String collectionName, String propertyName, Object value){
return key(applicationId, collectionName, propertyName, value);
}
@Metered(group="core",name="EntityManager_createAlias")
public UUID createAlias(Mutator mutator, UUID ownerId, EntityRef ref, String aliasType,
String alias, long timestamp) throws Exception {
UUID entityId = ref.getUuid();
String entityType = ref.getType();
if (entityType == null) {
entityType = getEntityType(entityId);
}
if (entityType == null) {
return null;
}
if (aliasType == null) {
aliasType = entityType;
}
if (ownerId == null) {
ownerId = applicationId;
}
if (alias == null) {
return null;
}
alias = alias.toLowerCase().trim();
UUID keyId = CassandraPersistenceUtils.aliasID(ownerId, aliasType,
alias);
addInsertToMutator(mutator, ENTITY_ALIASES, keyId, "entityId", entityId,
timestamp);
addInsertToMutator(mutator, ENTITY_ALIASES, keyId, "entityType", entityType,
timestamp);
addInsertToMutator(mutator, ENTITY_ALIASES, keyId, "aliasType", aliasType,
timestamp);
addInsertToMutator(mutator, ENTITY_ALIASES, keyId, "alias", alias, timestamp);
return keyId;
}
@Override
@Metered(group="core", name="EntityManager_deleteAlias")
public void deleteAlias(UUID ownerId, String aliasType, String alias)
throws Exception {
if (ownerId == null) {
ownerId = applicationId;
}
if (aliasType == null) {
return;
}
if (alias == null) {
return;
}
alias = alias.toLowerCase().trim();
UUID keyId = CassandraPersistenceUtils.aliasID(ownerId, aliasType,
alias);
cass.deleteRow(cass.getApplicationKeyspace(applicationId),
ENTITY_ALIASES, keyId);
}
@Override
@Metered(group="core", name="EntityManager_getAlias_single")
public EntityRef getAlias(UUID ownerId, String aliasType, String alias)
throws Exception {
if (ownerId == null) {
ownerId = applicationId;
}
if (aliasType == null) {
return null;
}
if (alias == null) {
return null;
}
alias = alias.toLowerCase().trim();
UUID keyId = CassandraPersistenceUtils.aliasID(ownerId, aliasType,
alias);
Set columnNames = new LinkedHashSet();
columnNames.add("entityId");
columnNames.add("entityType");
List> columns = cass.getColumns(
cass.getApplicationKeyspace(applicationId), ENTITY_ALIASES,
keyId, columnNames, se, be);
if (columns != null) {
Map cols = CassandraPersistenceUtils
.getColumnMap(columns);
String entityType = string(cols.get("entityType"));
UUID entityId = uuid(cols.get("entityId"), null);
if ((entityId != null) && (entityType != null)) {
return ref(entityType, entityId);
}
}
return null;
}
@Override
public Map getAlias(String aliasType,
List aliases) throws Exception {
return getAlias(applicationId, aliasType, aliases);
}
@Override
@Metered(group="core", name="EntityManager_getAlias_multi")
public Map getAlias(UUID ownerId, String aliasType,
List aliases) throws Exception {
if (ownerId == null) {
ownerId = applicationId;
}
if (aliasType == null) {
return null;
}
if (aliases == null) {
return null;
}
List keyIds = new ArrayList();
for (String alias : aliases) {
if (alias != null) {
alias = alias.toLowerCase().trim();
UUID keyId = CassandraPersistenceUtils.aliasID(ownerId,
aliasType, alias);
keyIds.add(keyId);
}
}
if (keyIds.size() == 0) {
return null;
}
Set columnNames = new LinkedHashSet();
columnNames.add("entityId");
columnNames.add("entityType");
columnNames.add("alias");
Rows rows = cass.getRows(
cass.getApplicationKeyspace(applicationId), ENTITY_ALIASES,
keyIds, columnNames, ue, se, be);
Map aliasedEntities = new HashMap();
for (Row row : rows) {
ColumnSlice slice = row.getColumnSlice();
if (slice == null) {
continue;
}
List> columns = slice.getColumns();
if (columns != null) {
Map cols = CassandraPersistenceUtils
.getColumnMap(columns);
String entityType = string(cols.get("entityType"));
UUID entityId = uuid(cols.get("entityId"), null);
String alias = string(cols.get("alias"));
if ((entityId != null) && (entityType != null)
&& (alias != null)) {
aliasedEntities.put(alias, ref(entityType, entityId));
}
}
}
return aliasedEntities;
}
@Metered(group="core", name="EntityManager_getAliases")
public List getAliases(UUID entityId) {
Keyspace ko = cass.getApplicationKeyspace(applicationId);
List aliases = new ArrayList();
IndexedSlicesQuery q = createIndexedSlicesQuery(ko,
ue, se, ue);
q.setColumnFamily(ENTITY_ALIASES.toString());
q.setColumnNames("entityId");
q.addEqualsExpression("entityId", entityId);
QueryResult> r = q.execute();
OrderedRows rows = r.get();
for (Row row : rows) {
UUID aliasId = row.getKey();
aliases.add(aliasId);
}
return aliases;
}
@Metered(group="core",name="EntityManager_deleteAliasesForEntity")
public void deleteAliasesForEntity(Mutator mutator, UUID entityId, long timestamp)
throws Exception {
List aliases = getAliases(entityId);
for (UUID alias : aliases) {
addDeleteToMutator(mutator, ENTITY_ALIASES, alias, timestamp);
}
}
@SuppressWarnings("unchecked")
@Override
public A create(String entityType, Class entityClass,
Map properties) throws Exception {
if ((entityType != null)
&& (entityType.startsWith(TYPE_ENTITY) || entityType
.startsWith("entities"))) {
throw new IllegalArgumentException("Invalid entity type");
}
A e = null;
try {
e = (A) create(entityType, (Class) entityClass, properties,
null);
} catch (ClassCastException e1) {
logger.error("Unable to create typed entity", e1);
}
return e;
}
@Override
public Entity create(UUID importId, String entityType,
Map properties) throws Exception {
return create(entityType, null, properties, importId);
}
@SuppressWarnings("unchecked")
@Override
public A create(A entity) throws Exception {
return (A) create(entity.getType(), entity.getClass(),
entity.getProperties());
}
@Override
@TraceParticipant
public Entity create(String entityType, Map properties)
throws Exception {
return create(entityType, null, properties);
}
/**
* Creates a new entity.
*
* @param
* the generic type
* @param applicationId
* the application id
* @param entityType
* the entity type
* @param entityClass
* the entity class
* @param properties
* the properties
* @param importId
* an existing external uuid to use as the id for the new entity
* @return new entity
* @throws Exception
* the exception
*/
@Metered(group="core",name="EntityManager_create")
@TraceParticipant
public A create(String entityType, Class entityClass,
Map properties, UUID importId) throws Exception {
UUID timestampUuid = newTimeUUID();
Keyspace ko = cass.getApplicationKeyspace(applicationId);
Mutator m = createMutator(ko, be);
A entity = batchCreate(m, entityType, entityClass, properties,
importId, timestampUuid);
batchExecute(m, CassandraService.RETRY_COUNT);
return entity;
}
@SuppressWarnings("unchecked")
@Metered(group = "core", name = "EntityManager_batchCreate")
public A batchCreate(Mutator m,
String entityType, Class entityClass,
Map properties, UUID importId, UUID timestampUuid)
throws Exception {
String eType = Schema.normalizeEntityType(entityType);
Schema schema = getDefaultSchema();
boolean is_application = TYPE_APPLICATION.equals(eType);
if (((applicationId == null) || applicationId
.equals(UUIDUtils.zeroUUID)) && !is_application) {
return null;
}
long timestamp = getTimestampInMicros(timestampUuid);
UUID itemId = UUIDUtils.newTimeUUID();
if (is_application) {
itemId = applicationId;
}
if (importId != null) {
itemId = importId;
}
boolean emptyPropertyMap = false;
if (properties == null) {
properties = new TreeMap(CASE_INSENSITIVE_ORDER);
}
if(properties.isEmpty()) {
emptyPropertyMap = true;
}
if (importId != null) {
if (isTimeBased(importId)) {
timestamp = UUIDUtils.getTimestampInMicros(importId);
} else if (properties.get(PROPERTY_CREATED) != null) {
timestamp = getLong(properties.get(PROPERTY_CREATED)) * 1000;
}
}
if (entityClass == null) {
entityClass = (Class) DynamicEntity.class;
}
Set required = schema.getRequiredProperties(
entityType);
if (required != null) {
for (String p : required) {
if (!PROPERTY_UUID.equals(p) && !PROPERTY_TYPE.equals(p)
&& !PROPERTY_CREATED.equals(p)
&& !PROPERTY_MODIFIED.equals(p)) {
Object v = properties.get(p);
if (schema.isPropertyTimestamp(entityType, p)) {
if (v == null) {
properties.put(p, timestamp / 1000);
} else {
long ts = getLong(v);
if (ts <= 0) {
properties.put(p, timestamp / 1000);
}
}
continue;
}
if (v == null) {
throw new RequiredPropertyNotFoundException(entityType,
p);
} else if ((v instanceof String) && isBlank((String) v)) {
throw new RequiredPropertyNotFoundException(entityType,
p);
}
}
}
}
// Create collection name based on entity: i.e. "users"
String collection_name = Schema.defaultCollectionName(eType);
// Create collection key based collection name
String bucketId = indexBucketLocator.getBucket(applicationId,
IndexType.COLLECTION, itemId, collection_name);
Object collection_key = key(applicationId,
Schema.DICTIONARY_COLLECTIONS, collection_name, bucketId);
CollectionInfo collection = null;
if (!is_application) {
// Add entity to collection
collection = schema.getCollection(TYPE_APPLICATION,
collection_name);
if(!emptyPropertyMap) {
addInsertToMutator(m, ENTITY_ID_SETS, collection_key, itemId, null,
timestamp);
}
// Add name of collection to dictionary property
// Application.collections
addInsertToMutator(m, ENTITY_DICTIONARIES,
key(applicationId, Schema.DICTIONARY_COLLECTIONS),
collection_name, null, timestamp);
addInsertToMutator(m, ENTITY_COMPOSITE_DICTIONARIES,
key(itemId, Schema.DICTIONARY_CONTAINER_ENTITIES),
asList(TYPE_APPLICATION, collection_name, applicationId),
null, timestamp);
// If the collection has subkeys, find each subkey variant
// and insert into the subkeyed collection as well
if (collection != null) {
if (collection.hasSubkeys()) {
List combos = collection.getSubkeyCombinations();
for (String[] combo : combos) {
List