All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy