com.buschmais.xo.impl.XOManagerImpl Maven / Gradle / Ivy
The newest version!
package com.buschmais.xo.impl;
import java.util.*;
import javax.validation.ConstraintViolation;
import com.buschmais.xo.api.*;
import com.buschmais.xo.api.metadata.MetadataProvider;
import com.buschmais.xo.api.metadata.method.AbstractRelationPropertyMethodMetadata;
import com.buschmais.xo.api.metadata.method.IndexedPropertyMethodMetadata;
import com.buschmais.xo.api.metadata.method.PrimitivePropertyMethodMetadata;
import com.buschmais.xo.api.metadata.type.*;
import com.buschmais.xo.impl.instancelistener.InstanceListenerService;
import com.buschmais.xo.impl.proxy.InstanceInvocationHandler;
import com.buschmais.xo.impl.proxy.example.ExampleProxyMethodService;
import com.buschmais.xo.impl.proxy.repository.RepositoryInvocationHandler;
import com.buschmais.xo.impl.proxy.repository.RepositoryProxyMethodService;
import com.buschmais.xo.impl.query.XOQueryImpl;
import com.buschmais.xo.impl.transaction.TransactionalResultIterator;
import com.buschmais.xo.spi.datastore.DatastoreRelationManager;
import com.buschmais.xo.spi.datastore.DatastoreSession;
import com.buschmais.xo.spi.session.InstanceManager;
import com.buschmais.xo.spi.session.XOSession;
import static com.buschmais.xo.api.Query.Result.CompositeRowObject;
import static com.buschmais.xo.api.metadata.type.RelationTypeMetadata.Direction.FROM;
import static com.buschmais.xo.api.metadata.type.RelationTypeMetadata.Direction.TO;
import static java.util.Collections.emptyMap;
/**
* Generic implementation of a {@link com.buschmais.xo.api.XOManager}.
*
* @param
* The type of entity ids as provided by the datastore.
* @param
* The type entities as provided by the datastore.
* @param
* The type of entity metadata as provided by the datastore.
* @param
* The type of discriminators as provided by the datastore.
* @param
* The type of relation ids as provided by the datastore.
* @param
* The type of relations as provided by the datastore.
* @param
* The type of relation metadata as provided by the datastore.
* @param
* The type of relation discriminators as provided by the datastore.
* @param
* The type of property metadata as provided by the datastore.
*/
public class XOManagerImpl, EntityDiscriminator, RelationId, Relation, RelationMetadata extends DatastoreRelationMetadata, RelationDiscriminator, PropertyMetadata>
implements XOManager {
private final SessionContext sessionContext;
private final XOSession session;
private final Map, Object> repositories = new HashMap<>();
private final Map, ExampleProxyMethodService>> exampleProxyMethodServices = new HashMap<>();
private final DefaultCloseSupport closeSupport = new DefaultCloseSupport();
/**
* Constructor.
*
* @param sessionContext
* The associated {@link SessionContext}.
*/
public XOManagerImpl(
SessionContext sessionContext) {
this.sessionContext = sessionContext;
this.session = new XOSessionImpl<>(sessionContext);
}
@Override
public XOTransaction currentTransaction() {
return sessionContext.getXOTransaction();
}
@Override
public Set> validate() {
return sessionContext.getInstanceValidationService()
.validate();
}
@Override
public T findById(Class type, I id) {
TypeMetadata typeMetadata = sessionContext.getMetadataProvider()
.getRegisteredMetadata()
.get(type);
if (typeMetadata == null) {
throw new XOException(type.getName() + " is not a registered type.");
} else if (typeMetadata instanceof SimpleTypeMetadata) {
throw new XOException(type.getName() + " must either be an entity or relation type.");
} else if (typeMetadata instanceof EntityTypeMetadata) {
EntityTypeMetadata entityTypeMetadata = (EntityTypeMetadata) typeMetadata;
Entity entityById = sessionContext.getDatastoreSession()
.getDatastoreEntityManager()
.findEntityById(entityTypeMetadata, entityTypeMetadata.getDatastoreMetadata()
.getDiscriminator(), (EntityId) id);
return sessionContext.getEntityInstanceManager()
.readInstance(entityById);
} else if (typeMetadata instanceof RelationTypeMetadata) {
RelationTypeMetadata relationTypeMetadata = (RelationTypeMetadata) typeMetadata;
Relation relationById = sessionContext.getDatastoreSession()
.getDatastoreRelationManager()
.findRelationById(relationTypeMetadata, (RelationId) id);
return sessionContext.getRelationInstanceManager()
.readInstance(relationById);
}
throw new XOException("Unsupported metadata type: " + typeMetadata);
}
@Override
public ResultIterable find(final Class type, final Object value) {
sessionContext.getCacheSynchronizationService()
.flush();
EntityTypeMetadata entityTypeMetadata = sessionContext.getMetadataProvider()
.getEntityMetadata(type);
IndexedPropertyMethodMetadata indexedProperty = entityTypeMetadata.getIndexedProperty();
Map, Object> exampleEntity = new HashMap<>(1);
if (indexedProperty != null) {
exampleEntity.put(indexedProperty.getPropertyMethodMetadata(), value);
} else {
exampleEntity.put(null, value);
}
return findByExample(type, exampleEntity);
}
@Override
public ResultIterable find(Example example, Class type) {
Map, Object> exampleEntity = prepareExample(example, type);
return findByExample(type, exampleEntity);
}
@Override
public ResultIterable find(Example example, Class> type, Class>... types) {
Map, Object> exampleEntity = prepareExample(example, type, types);
return findByExample(type, exampleEntity);
}
@Override
public ResultIterable find(Class type, Example example) {
Map, Object> exampleEntity = prepareExample(example, type);
return findByExample(type, exampleEntity);
}
/**
* Setup an example entity.
*
* @param type
* The type.
* @param example
* The provided example.
* @param
* The type.
* @return The example.
*/
private Map, Object> prepareExample(Example example, Class> type, Class>... types) {
Map, Object> exampleEntity = new HashMap<>();
ExampleProxyMethodService proxyMethodService = (ExampleProxyMethodService) exampleProxyMethodServices.computeIfAbsent(type,
k -> new ExampleProxyMethodService(type, sessionContext));
InstanceInvocationHandler invocationHandler = new InstanceInvocationHandler(exampleEntity, proxyMethodService);
List> effectiveTypes = new ArrayList<>(types.length + 1);
effectiveTypes.add(type);
effectiveTypes.addAll(Arrays.asList(types));
CompositeType compositeType = CompositeType.builder()
.type(CompositeObject.class)
.type(type)
.types(types)
.build();
T instance = sessionContext.getProxyFactory()
.createInstance(invocationHandler, compositeType);
example.prepare(instance);
return exampleEntity;
}
/**
* Find entities according to the given example entity.
*
* @param type
* The entity type.
* @param entity
* The example entity.
* @param
* The entity type.
* @return A {@link ResultIterable}.
*/
private ResultIterable findByExample(Class> type, Map, Object> entity) {
sessionContext.getCacheSynchronizationService()
.flush();
EntityTypeMetadata entityTypeMetadata = sessionContext.getMetadataProvider()
.getEntityMetadata(type);
EntityDiscriminator entityDiscriminator = entityTypeMetadata.getDatastoreMetadata()
.getDiscriminator();
if (entityDiscriminator == null) {
throw new XOException("Type " + type.getName() + " has no discriminator (i.e. cannot be identified in datastore).");
}
ResultIterator iterator = sessionContext.getDatastoreSession()
.getDatastoreEntityManager()
.findEntity(entityTypeMetadata, entityDiscriminator, entity);
AbstractInstanceManager entityInstanceManager = sessionContext.getEntityInstanceManager();
ResultIterator resultIterator = new ResultIterator() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public T next() {
Entity entity = iterator.next();
return entityInstanceManager.readInstance(entity);
}
@Override
public void remove() {
throw new UnsupportedOperationException("Cannot remove instance.");
}
@Override
public void close() {
iterator.close();
}
};
XOTransaction xoTransaction = sessionContext.getXOTransaction();
final ResultIterator transactionalIterator =
xoTransaction != null ? new TransactionalResultIterator<>(resultIterator, xoTransaction) : resultIterator;
return sessionContext.getInterceptorFactory()
.addInterceptor(new AbstractResultIterable() {
@Override
public ResultIterator iterator() {
return transactionalIterator;
}
}, ResultIterable.class);
}
@Override
public CompositeObject create(Class> type, Class>... types) {
return createByExample(emptyMap(), type, types);
}
@Override
public T create(Class type, Example example, Class>... types) {
Map, Object> exampleEntity = prepareExample(example, type);
return createByExample(exampleEntity, type, types).as(type);
}
public T create(Class type) {
return createByExample(emptyMap(), type).as(type);
}
/**
* Create a new {@link CompositeObject} instance using an example.
*
* @param exampleEntity
* The example instance.
* @param type
* The interface the property type shall implement.
* @param types
* Additional interfaces the entity type shall implement.
* @return The {@link CompositeObject} instance.
*/
private CompositeObject createByExample(Map, Object> exampleEntity, Class> type, Class>... types) {
CompositeTypeMetadata> effectiveTypes = sessionContext.getMetadataProvider()
.getEffectiveTypes(type, types);
Set entityDiscriminators = sessionContext.getMetadataProvider()
.getEntityDiscriminators(effectiveTypes);
DatastoreSession datastoreSession = sessionContext.getDatastoreSession();
Entity entity = datastoreSession.getDatastoreEntityManager()
.createEntity(effectiveTypes, entityDiscriminators, exampleEntity);
AbstractInstanceManager entityInstanceManager = sessionContext.getEntityInstanceManager();
CompositeObject instance = entityInstanceManager.createInstance(entity, effectiveTypes);
sessionContext.getInstanceListenerService()
.postCreate(instance);
return instance;
}
@Override
public R create(S from, Class relationType, T to) {
return createByExample(from, relationType, to, Collections.emptyMap());
}
@Override
public R create(S from, Class relationType, T to, Example example) {
Map, Object> exampleRelation = prepareExample(example, relationType);
return createByExample(from, relationType, to, exampleRelation);
}
private R createByExample(S from, Class relationType, T to, Map, Object> example) {
MetadataProvider metadataProvider = sessionContext.getMetadataProvider();
AbstractRelationPropertyMethodMetadata> fromProperty = metadataProvider.getPropertyMetadata(from.getClass(), relationType, FROM);
AbstractRelationPropertyMethodMetadata> toProperty = metadataProvider.getPropertyMetadata(to.getClass(), relationType, TO);
Entity entity = sessionContext.getEntityInstanceManager()
.getDatastoreType(from);
R instance = sessionContext.getEntityPropertyManager()
.createRelationReference(entity, fromProperty, to, toProperty, example);
sessionContext.getInstanceListenerService()
.postCreate(instance);
return instance;
}
@Override
public T getRepository(Class repositoryType) {
T repository = (T) repositories.get(repositoryType);
if (repository == null) {
T datastoreRepository = sessionContext.getDatastoreSession()
.createRepository(session, repositoryType);
RepositoryProxyMethodService proxyMethodService;
if (repositoryType.isAssignableFrom(datastoreRepository.getClass())) {
proxyMethodService = new RepositoryProxyMethodService<>(datastoreRepository, repositoryType);
} else {
RepositoryTypeMetadata repositoryMetadata = sessionContext.getMetadataProvider()
.getRepositoryMetadata(repositoryType);
proxyMethodService = new RepositoryProxyMethodService<>(datastoreRepository, repositoryMetadata, sessionContext);
}
RepositoryInvocationHandler invocationHandler = new RepositoryInvocationHandler(proxyMethodService, this);
T instance = sessionContext.getProxyFactory()
.createInstance(invocationHandler, CompositeType.builder()
.type(repositoryType)
.build());
repository = sessionContext.getInterceptorFactory()
.addInterceptor(instance, repositoryType);
repositories.put(repositoryType, repository);
}
return repository;
}
@Override
public Id getId(T instance) {
InstanceManager entityInstanceManager = sessionContext.getEntityInstanceManager();
InstanceManager relationInstanceManager = sessionContext.getRelationInstanceManager();
if (entityInstanceManager.isInstance(instance)) {
Entity entity = entityInstanceManager.getDatastoreType(instance);
return (Id) sessionContext.getDatastoreSession()
.getDatastoreEntityManager()
.getEntityId(entity);
} else if (relationInstanceManager.isInstance(instance)) {
Relation relation = relationInstanceManager.getDatastoreType(instance);
return (Id) sessionContext.getDatastoreSession()
.getDatastoreRelationManager()
.getRelationId(relation);
}
throw new XOException(instance + " is not a managed XO instance.");
}
@Override
public XOMigrator migrate(T instance) {
return sessionContext.getInterceptorFactory()
.addInterceptor(new XOMigratorImpl<>(instance, sessionContext));
}
@Override
public void delete(T instance) {
InstanceListenerService instanceListenerService = sessionContext.getInstanceListenerService();
InstanceManager entityInstanceManager = sessionContext.getEntityInstanceManager();
InstanceManager relationInstanceManager = sessionContext.getRelationInstanceManager();
DatastoreSession datastoreSession = sessionContext.getDatastoreSession();
if (entityInstanceManager.isInstance(instance)) {
Entity entity = entityInstanceManager.getDatastoreType(instance);
instanceListenerService.preDelete(instance);
datastoreSession.getDatastoreEntityManager()
.deleteEntity(entity);
entityInstanceManager.removeInstance(instance);
entityInstanceManager.closeInstance(instance);
instanceListenerService.postDelete(instance);
} else if (relationInstanceManager.isInstance(instance)) {
DatastoreRelationManager datastoreRelationManager = datastoreSession.getDatastoreRelationManager();
Relation relation = relationInstanceManager.getDatastoreType(instance);
Entity from = datastoreRelationManager.getFrom(relation);
Entity to = datastoreRelationManager.getTo(relation);
instanceListenerService.preDelete(instance);
datastoreRelationManager.getRelationDiscriminator(relation);
datastoreRelationManager.deleteRelation(relation);
relationInstanceManager.removeInstance(instance);
relationInstanceManager.closeInstance(instance);
entityInstanceManager.updateInstance(from);
entityInstanceManager.updateInstance(to);
instanceListenerService.postDelete(instance);
} else {
throw new XOException(instance + " is not a managed XO instance.");
}
}
@Override
public Query createQuery(String query) {
XOQueryImpl xoQuery = new XOQueryImpl<>(sessionContext, query);
return sessionContext.getInterceptorFactory()
.addInterceptor(xoQuery, Query.class);
}
@Override
public Query createQuery(String query, Class type) {
XOQueryImpl xoQuery = new XOQueryImpl<>(sessionContext, query, type);
return sessionContext.getInterceptorFactory()
.addInterceptor(xoQuery, Query.class);
}
@Override
public Query createQuery(String query, Class> type, Class>... types) {
XOQueryImpl xoQuery = new XOQueryImpl<>(sessionContext, query, type, Arrays.asList(types));
return sessionContext.getInterceptorFactory()
.addInterceptor(xoQuery, Query.class);
}
@Override
public Query createQuery(Class query) {
XOQueryImpl, Entity, Relation> xoQuery = new XOQueryImpl<>(sessionContext, query, query);
return sessionContext.getInterceptorFactory()
.addInterceptor(xoQuery, Query.class);
}
@Override
public Query createQuery(Class query, Class>... types) {
XOQueryImpl, Entity, Relation> xoQuery = new XOQueryImpl<>(sessionContext, query, query, Arrays.asList(types));
return sessionContext.getInterceptorFactory()
.addInterceptor(xoQuery, Query.class);
}
@Override
public void close() {
fireOnBeforeClose();
sessionContext.getEntityInstanceManager()
.close();
sessionContext.getRelationInstanceManager()
.close();
sessionContext.getDatastoreSession()
.close();
fireOnAfterClose();
}
@Override
public DS getDatastoreSession(Class sessionType) {
DatastoreSession datastoreSession = sessionContext.getDatastoreSession();
return sessionType.cast(datastoreSession);
}
@Override
public void flush() {
sessionContext.getCacheSynchronizationService()
.flush();
}
@Override
public void clear() {
sessionContext.getCacheSynchronizationService()
.clear();
}
@Override
public void registerInstanceListener(I instanceListener) {
sessionContext.getInstanceListenerService()
.registerInstanceListener(instanceListener);
}
@Override
public void addCloseListener(CloseListener listener) {
closeSupport.addCloseListener(listener);
}
@Override
public void removeCloseListener(CloseListener listener) {
closeSupport.removeCloseListener(listener);
}
private void fireOnBeforeClose() {
closeSupport.fireOnBeforeClose();
}
private void fireOnAfterClose() {
closeSupport.fireOnAfterClose();
}
}