step.core.entities.EntityDependencyTreeVisitor Maven / Gradle / Ivy
The newest version!
package step.core.entities;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import step.core.accessors.AbstractIdentifiableObject;
import step.core.accessors.Accessor;
import step.core.dynamicbeans.DynamicValue;
import step.core.objectenricher.ObjectPredicate;
public class EntityDependencyTreeVisitor {
private static final Logger logger = LoggerFactory.getLogger(EntityDependencyTreeVisitor.class);
private final EntityManager entityManager;
private final ObjectPredicate objectPredicate;
// Declared as static for performance reasons. In the current implementation, this class gets instantiated quite often
// TODO declare it as non-static to avoid potential leaks
private static final Map, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
public EntityDependencyTreeVisitor(EntityManager entityManager, ObjectPredicate objectPredicate) {
super();
this.entityManager = entityManager;
this.objectPredicate = objectPredicate;
}
public void visitEntityDependencyTree(String entityName, String entityId, EntityTreeVisitor visitor,
boolean recursive) {
EntityTreeVisitorContext context = new EntityTreeVisitorContext(objectPredicate, recursive, visitor, null);
visitEntity(entityName, entityId, context);
}
public void visitSingleObject(Object object, EntityTreeVisitor visitor, Set messageCollector) {
EntityTreeVisitorContext context = new EntityTreeVisitorContext(objectPredicate, false, visitor, messageCollector);
resolveEntityDependencies(object, context);
}
public class EntityTreeVisitorContext {
private final boolean recursive;
private final ObjectPredicate objectPredicate;
private final EntityTreeVisitor visitor;
private final Map stack = new HashMap<>();
private final Set messageCollector;
public EntityTreeVisitorContext(ObjectPredicate objectPredicate, boolean recursive, EntityTreeVisitor visitor, Set messageCollector) {
super();
this.objectPredicate = objectPredicate;
this.recursive = recursive;
this.visitor = visitor;
this.messageCollector = messageCollector;
}
public ObjectPredicate getObjectPredicate() {
return objectPredicate;
}
public void visitEntity(String entityName, String entityId) {
if (recursive) {
EntityDependencyTreeVisitor.this.visitEntity(entityName, entityId, this);
}
}
public String resolvedEntityId(String entityName, String entityId) {
return visitor.onResolvedEntityId(entityName, entityId);
}
protected EntityTreeVisitor getVisitor() {
return visitor;
}
public boolean isRecursive() {
return recursive;
}
protected Map getStack() {
return stack;
}
public Set getMessageCollector() {
return messageCollector;
}
}
public interface EntityTreeVisitor {
void onWarning(String warningMessage);
void onResolvedEntity(String entityName, String entityId, Object entity);
String onResolvedEntityId(String entityName, String entityId);
}
private void visitEntity(String entityName, String entityId, EntityTreeVisitorContext context) {
Entity, ?> entityType = entityManager.getEntityByName(entityName);
if (entityType == null) {
String error = "Entities of type '" + entityName + "' are not supported";
logger.error(error);
throw new RuntimeException(error);
}
EntityTreeVisitor visitor = context.getVisitor();
Accessor> accessor = entityType.getAccessor();
AbstractIdentifiableObject entity = accessor.get(entityId);
Map stack = context.getStack();
// avoid infinite recursions
if (!stack.containsKey(entityId)) {
stack.put(entityId, entity);
if (entity == null) {
String warning = "Referenced entity with id '" + entityId + "' and type '" + entityName
+ "' is missing";
logger.warn(warning);
visitor.onWarning(warning);
} else {
visitor.onResolvedEntity(entityName, entityId, entity);
resolveEntityDependencies(entity, context);
}
stack.remove(entityId);
}
}
@SuppressWarnings("unchecked")
private void resolveEntityDependencies(Object entity, EntityTreeVisitorContext context) {
if (entity != null) {
if (logger.isDebugEnabled()) {
logger.debug("Resolving dependencies for object " + entity);
}
EntityTreeVisitor visitor = context.getVisitor();
BeanInfo beanInfo = getBeanInfo(entity.getClass(), visitor);
entityManager.getDependencyTreeVisitorHooks().forEach(h -> h.onVisitEntity(entity, context));
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
Method method = descriptor.getReadMethod();
if (method != null && method.isAnnotationPresent(EntityReference.class)) {
EntityReference entityReferenceAnnotation = method.getAnnotation(EntityReference.class);
String entityType = entityReferenceAnnotation.type();
Object value = null;
try {
value = method.invoke(entity);
} catch (IllegalAccessException | InvocationTargetException e) {
visitor.onWarning("IllegalAccessException failed for method " + method.getName());
}
if (entityType.equals(EntityManager.recursive)) {
// No actual references, but need to process the field recursively
if (value instanceof Collection) {
Collection> c = (Collection>) value;
c.forEach(o -> resolveEntityDependencies(o, context));
} else {
resolveEntityDependencies(value, context);
}
} else {
if (value instanceof Collection) {
Collection> c = (Collection>) value;
AtomicBoolean listUpdated = new AtomicBoolean();
ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy