com.terracotta.entity.ClusteredEntityManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/*
* All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
*/
package com.terracotta.entity;
import org.terracotta.toolkit.Toolkit;
import org.terracotta.toolkit.collections.ToolkitMap;
import org.terracotta.toolkit.concurrent.locks.ToolkitLock;
import org.terracotta.toolkit.concurrent.locks.ToolkitReadWriteLock;
import com.terracotta.entity.internal.InternalRootEntity;
import com.terracotta.entity.internal.LockingEntity;
import com.terracotta.entity.internal.ToolkitAwareEntity;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
/**
* ClusteredEntityManager
*/
public class ClusteredEntityManager {
private static final long TRY_LOCK_TIMEOUT_SECONDS = 2;
private final Toolkit toolkit;
private final EntityLockHandler entityLockHandler;
private volatile transient ConcurrentMap> entityMapsMap = new ConcurrentHashMap>();
public ClusteredEntityManager(Toolkit toolkit) {
this(toolkit, new EntityLockHandler(toolkit));
}
ClusteredEntityManager(Toolkit toolkit, EntityLockHandler entityLockHandler) {
this.toolkit = toolkit;
this.entityLockHandler = entityLockHandler;
}
public T getRootEntity(String name, Class entityClass) {
T entity = getRootEntityInternal(name, entityClass);
if (entity != null && ClusteredEntityState.DESTROY_IN_PROGRESS.equals(entity.getState())) {
destroyRootEntitySilently(name, entityClass, entity);
return null;
}
return entity;
}
public Map getRootEntities(Class entityClass) {
HashMap resultMap = new HashMap();
for (Map.Entry entry : getEntityMap(entityClass).entrySet()) {
T entity = entry.getValue();
if (!ClusteredEntityState.DESTROY_IN_PROGRESS.equals(entity.getState())) {
resultMap.put(entry.getKey(), processEntity(entity));
} else {
destroyRootEntitySilently(entry.getKey(), entityClass, entity);
}
}
return Collections.unmodifiableMap(resultMap);
}
/**
* Method for adding a {@link com.terracotta.entity.RootEntity} to this ClusteredEntityManager.
*
* If a {@link com.terracotta.entity.RootEntity} of the same clusteredEntityClass with the same name is already known
* to this ClusteredEntityManager, that entity will be returned and the clusteredEntity passed in will not added.
*
* @param name the name of the entity
* @param clusteredEntityClass the type of the entity
* @param clusteredEntity the clustered entity
*
* @return the current known mapping if any, {@code null} otherwise
*/
public T addRootEntityIfAbsent(String name, Class clusteredEntityClass, T clusteredEntity) {
ToolkitMap map = getEntityMap(clusteredEntityClass);
T oldValue = map.putIfAbsent(name, clusteredEntity);
if (oldValue != null) {
return processEntity(oldValue);
} else {
processEntity(clusteredEntity);
return null;
}
}
/**
* Method for destroying a root entity
*
* This method will follow these steps:
*
* - Put entity in exclusive maintenance mode
* If this fails, throws {@link java.lang.IllegalStateException}
* - Verify the entity passed in matches the current known entity
* If this fails, throws {@link java.lang.IllegalArgumentException}
* - Mark the entity with {@link com.terracotta.entity.ClusteredEntityState#DESTROY_IN_PROGRESS} and save it
* If this fails, throws {@link java.lang.UnsupportedOperationException}
* - Perform the destroy operation
*
*
* @param name name of the entity to destroy
* @param rootEntityClass public interface under which this entity is managed
* @param controlEntity the entity to destroy
* @param The managed entity type
* @return {@code true} if entity was effectively destroyed, {@code false} if entity does not exist
*/
public boolean destroyRootEntity(String name, Class rootEntityClass, T controlEntity) {
InternalRootEntity currentRootEntity = asInternalRootEntity(getRootEntityInternal(name, rootEntityClass));
if (currentRootEntity != null) {
ToolkitLock entityWriteLock = currentRootEntity.getEntityLock().writeLock();
try {
if (entityWriteLock.tryLock(TRY_LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
try {
if (!currentRootEntity.equals(controlEntity)) {
throw new IllegalArgumentException(String.format("The specified entity named %s does not match " +
"the mapping known to this entity manager", name));
}
currentRootEntity.markDestroying();
try {
getEntityMap(rootEntityClass).put(name, (T) currentRootEntity);
} catch (Exception e) {
// Failed to save entity in destroy state - abort
currentRootEntity.alive();
throw new UnsupportedOperationException(String.format("Unable to mark entity %s of type %s with destroy in progress", name, rootEntityClass), e);
}
currentRootEntity.destroy();
getEntityMap(rootEntityClass).remove(name);
return true;
} finally {
entityWriteLock.unlock();
}
} else {
throw new IllegalStateException(String.format("Unable to lock entity %s of type %s for destruction", name, rootEntityClass));
}
} catch (InterruptedException e) {
throw new IllegalStateException(String.format("Unable to lock entity %s of type %s for destruction", name, rootEntityClass), e);
}
}
return false;
}
private InternalRootEntity asInternalRootEntity(T currentRootEntity) {
return (InternalRootEntity) currentRootEntity;
}
public ToolkitReadWriteLock getEntityLock(String lockName) {
return toolkit.getReadWriteLock(lockName);
}
public void dispose() {
entityLockHandler.dispose();
}
private T getRootEntityInternal(String name, Class rootEntityClass) {
return processEntity(getEntityMap(rootEntityClass).get(name));
}
private void destroyRootEntitySilently(String name, Class entityClass, T entity) {
try {
destroyRootEntity(name, entityClass, entity);
} catch (Exception e) {
// Ignore - trying to destroy left overs
}
}
private T processEntity(T entity) {
if (entity instanceof ToolkitAwareEntity) {
((ToolkitAwareEntity)entity).setToolkit(toolkit);
}
if (entity instanceof LockingEntity) {
((LockingEntity)entity).setEntityLockHandler(entityLockHandler);
}
return entity;
}
private ToolkitMap getEntityMap(Class entityClass) {
ToolkitMap entityMap = (ToolkitMap) entityMapsMap.get(entityClass);
if (entityMap == null) {
entityMap = toolkit.getMap(getMapName(entityClass), String.class, entityClass);
ToolkitMap installedMap = (ToolkitMap) entityMapsMap.putIfAbsent(entityClass, entityMap);
if (installedMap != null) {
entityMap = installedMap;
}
}
return entityMap;
}
String getMapName(Class entityClass) {
return entityClass.getName();
}
}