com.mmnaseri.dragonfly.runtime.session.impl.AbstractSessionPreparator.java Source code

Java tutorial

Introduction

Here is the source code for com.mmnaseri.dragonfly.runtime.session.impl.AbstractSessionPreparator.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2013 Milad Naseri.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.mmnaseri.dragonfly.runtime.session.impl;

import com.mmnaseri.couteau.basics.api.Transformer;
import com.mmnaseri.dragonfly.data.DataAccessSession;
import com.mmnaseri.dragonfly.data.DataAccessSessionAware;
import com.mmnaseri.dragonfly.data.DataStructureHandler;
import com.mmnaseri.dragonfly.data.impl.DefaultDataAccessSession;
import com.mmnaseri.dragonfly.dialect.DatabaseDialect;
import com.mmnaseri.dragonfly.entity.EntityDefinition;
import com.mmnaseri.dragonfly.entity.EntityDefinitionContext;
import com.mmnaseri.dragonfly.entity.ModifiableEntityContext;
import com.mmnaseri.dragonfly.entity.impl.DefaultEntityDefinitionContext;
import com.mmnaseri.dragonfly.entity.impl.ImmutableEntityDefinition;
import com.mmnaseri.dragonfly.ext.ExtensionManager;
import com.mmnaseri.dragonfly.ext.ExtensionMetadata;
import com.mmnaseri.dragonfly.ext.ExtensionMetadataResolver;
import com.mmnaseri.dragonfly.ext.impl.DefaultExtensionManager;
import com.mmnaseri.dragonfly.metadata.*;
import com.mmnaseri.dragonfly.metadata.impl.DefaultTableMetadataResolverContext;
import com.mmnaseri.dragonfly.runtime.lookup.LookupSource;
import com.mmnaseri.dragonfly.runtime.session.SessionInitializationEventHandler;
import com.mmnaseri.dragonfly.runtime.session.SessionPreparator;
import com.mmnaseri.dragonfly.statement.StatementRegistry;
import com.mmnaseri.dragonfly.statement.impl.StatementRegistryPreparator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;

import static com.mmnaseri.couteau.basics.collections.CollectionWrapper.with;

/**
 * @author Milad Naseri (mmnaseri@programmer.net)
 * @since 1.0 (14/8/13 AD, 18:51)
 */
public abstract class AbstractSessionPreparator implements SessionPreparator {

    private final static Log log = LogFactory.getLog(SessionPreparator.class);
    private String[] basePackages;
    private DatabaseDialect databaseDialect;
    private ModifiableEntityContext entityContext;
    private StatementRegistry statementRegistry;
    private TableMetadataRegistry tableMetadataRegistry;
    private ClassLoader parentClassLoader;
    private Set<LookupSource> entityDefinitionSources;
    private Set<LookupSource> extensionDefinitionSources;
    private final EntityDefinitionContext entityDefinitionContext;
    private final ExtensionManager extensionManager;
    private final TableMetadataResolverContext tableMetadataResolverContext;
    private boolean initializeSession;

    protected abstract List<TableMetadataResolver> getMetadataResolvers();

    protected abstract ExtensionMetadataResolver<Class<?>> getExtensionMetadataResolver();

    protected void addEntityDefinitionSource(LookupSource lookupSource) {
        entityDefinitionSources.add(lookupSource);
    }

    protected void addExtensionDefinitionSource(LookupSource lookupSource) {
        extensionDefinitionSources.add(lookupSource);
    }

    public AbstractSessionPreparator() {
        entityDefinitionSources = new CopyOnWriteArraySet<LookupSource>();
        extensionDefinitionSources = new CopyOnWriteArraySet<LookupSource>();
        entityDefinitionContext = new DefaultEntityDefinitionContext();
        extensionManager = new DefaultExtensionManager();
        tableMetadataResolverContext = new DefaultTableMetadataResolverContext(MetadataResolveStrategy.UNAMBIGUOUS,
                Arrays.<TableMetadataInterceptor>asList(extensionManager));
        entityDefinitionContext.addInterceptor(extensionManager);
        parentClassLoader = null;
    }

    @Override
    public void setDatabaseDialect(DatabaseDialect databaseDialect) {
        this.databaseDialect = databaseDialect;
    }

    @Override
    public void setEntityContext(ModifiableEntityContext entityContext) {
        this.entityContext = entityContext;
    }

    @Override
    public void setStatementRegistry(StatementRegistry statementRegistry) {
        this.statementRegistry = statementRegistry;
    }

    @Override
    public void setTableMetadataRegistry(TableMetadataRegistry tableMetadataRegistry) {
        this.tableMetadataRegistry = tableMetadataRegistry;
    }

    public void setParentClassLoader(ClassLoader parentClassLoader) {
        this.parentClassLoader = parentClassLoader;
    }

    public void setBasePackages(String[] basePackages) {
        this.basePackages = basePackages;
    }

    @Override
    public String[] getBasePackages() {
        return basePackages;
    }

    @Override
    public EntityDefinitionContext getEntityDefinitionContext() {
        return entityDefinitionContext;
    }

    @Override
    public TableMetadataRegistry getTableMetadataRegistry() {
        return tableMetadataRegistry;
    }

    @Override
    public ExtensionManager getExtensionManager() {
        return extensionManager;
    }

    @Override
    public DatabaseDialect getDatabaseDialect() {
        return databaseDialect;
    }

    public void setInitializeSession(boolean initializeSession) {
        this.initializeSession = initializeSession;
    }

    private void prepareResolvers(Collection<SessionInitializationEventHandler> eventHandlers) {
        final List<TableMetadataResolver> resolvers = getMetadataResolvers();
        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
            eventHandler.beforePreparingResolvers(resolvers);
        }
        for (TableMetadataResolver resolver : resolvers) {
            tableMetadataResolverContext.addMetadataResolver(resolver);
        }
        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
            eventHandler.afterPreparingResolvers(resolvers);
        }
    }

    private void registerEntities(Collection<SessionInitializationEventHandler> eventHandlers) {
        log.info("Finding entity classes ...");
        final List<Class> entityClasses = new ArrayList<Class>();
        for (LookupSource source : entityDefinitionSources) {
            Collections.addAll(entityClasses, source.getClasses(basePackages));
        }
        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
            eventHandler.beforeRegisteringEntities(entityDefinitionContext, entityClasses);
        }
        for (Class entityClass : entityClasses) {
            log.debug("Registering entity " + entityClass.getCanonicalName());
            //noinspection unchecked
            final ImmutableEntityDefinition<Object> entityDefinition = new ImmutableEntityDefinition<Object>(
                    entityClass, Collections.<Class<?>, Class<?>>emptyMap());
            for (SessionInitializationEventHandler eventHandler : eventHandlers) {
                eventHandler.beforeRegisteringEntity(entityDefinitionContext, entityDefinition);
            }
            entityDefinitionContext.addDefinition(entityDefinition);
            for (SessionInitializationEventHandler eventHandler : eventHandlers) {
                eventHandler.afterRegisteringEntity(entityDefinitionContext, entityDefinition);
            }
        }
        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
            eventHandler.afterRegisteringEntities(entityDefinitionContext, entityClasses);
        }
    }

    private void registerExtensions(Collection<SessionInitializationEventHandler> eventHandlers) {
        log.info("Finding extensions to the data access ...");
        final ExtensionMetadataResolver<Class<?>> metadataResolver = getExtensionMetadataResolver();
        final List<Class> extensionClasses = new ArrayList<Class>();
        for (LookupSource source : extensionDefinitionSources) {
            Collections.addAll(extensionClasses, source.getClasses(basePackages));
        }
        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
            eventHandler.beforeRegisteringExtensions(extensionManager, extensionClasses);
        }
        for (Class extensionDefinitionClass : extensionClasses) {
            log.debug("Registering extension " + extensionDefinitionClass.getCanonicalName());
            final ExtensionMetadata extensionMetadata = metadataResolver.resolve(extensionDefinitionClass);
            for (SessionInitializationEventHandler eventHandler : eventHandlers) {
                eventHandler.beforeRegisteringExtension(extensionManager, extensionMetadata);
            }
            extensionManager.addExtension(extensionMetadata);
            for (SessionInitializationEventHandler eventHandler : eventHandlers) {
                eventHandler.afterRegisteringExtension(extensionManager, extensionMetadata);
            }
        }
        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
            eventHandler.beforeRegisteringExtensions(extensionManager, extensionClasses);
        }
    }

    private void prepareStatements(Collection<SessionInitializationEventHandler> eventHandlers) {
        log.info("Preparing entity statements for later use");
        final StatementRegistryPreparator preparator = new StatementRegistryPreparator(databaseDialect,
                tableMetadataResolverContext, tableMetadataRegistry);
        for (Class<?> entityDefinitionClass : entityDefinitionContext.getEntities()) {
            preparator.addEntity(entityDefinitionClass);
        }
        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
            eventHandler.beforePreparingStatements(preparator, tableMetadataRegistry, statementRegistry);
        }
        preparator.prepare(statementRegistry);
        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
            eventHandler.afterPreparingStatements(preparator, tableMetadataRegistry, statementRegistry);
        }
    }

    private void registerInterfaces(final Collection<SessionInitializationEventHandler> eventHandlers) {
        log.info("Registering interfaces with the context");
        entityContext.setInterfaces(with(entityDefinitionContext.getEntities())
                .map(new Transformer<Class<?>, Map<Class<?>, Class<?>>>() {
                    @Override
                    public Map<Class<?>, Class<?>> map(Class<?> input) {
                        //noinspection unchecked
                        final EntityDefinition<Object> definition = extensionManager
                                .intercept(new ImmutableEntityDefinition<Object>((Class<Object>) input,
                                        Collections.<Class<?>, Class<?>>emptyMap()));
                        final Map<Class<?>, Class<?>> interfaces = definition.getInterfaces();
                        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
                            eventHandler.beforeDeterminingInterfaces(input, interfaces);
                        }
                        return interfaces;
                    }
                }));
    }

    private void initializeSession(DataAccessSession session, ConfigurableListableBeanFactory beanFactory,
            Collection<SessionInitializationEventHandler> eventHandlers) {
        if (initializeSession) {
            final DataStructureHandler dataStructureHandler;
            if (session instanceof DefaultDataAccessSession) {
                dataStructureHandler = ((DefaultDataAccessSession) session).getDataStructureHandler();
            } else {
                dataStructureHandler = null;
            }
            for (SessionInitializationEventHandler eventHandler : eventHandlers) {
                eventHandler.beforeInitialization(beanFactory, session, dataStructureHandler);
            }
            session.initialize();
            for (SessionInitializationEventHandler eventHandler : eventHandlers) {
                eventHandler.beforeSignalingInitialization(beanFactory, session, dataStructureHandler);
            }
            session.markInitialized();
            for (SessionInitializationEventHandler eventHandler : eventHandlers) {
                eventHandler.afterInitialization(beanFactory, session, dataStructureHandler);
            }
        }
    }

    private void handleClassLoader(ConfigurableListableBeanFactory beanFactory) {
        log.info("Setting the class loader for the entity context");
        entityContext.setDefaultClassLoader(beanFactory.getBeanClassLoader());
    }

    @Override
    public void postProcessSession(ConfigurableListableBeanFactory beanFactory) {
        log.warn(
                "Preparing the session at runtime can be harmful to your performance. You should consider switching to "
                        + "the Maven plugin.");
        log.debug("Looking up the necessary components ...");
        if (parentClassLoader == null) {
            log.debug("Falling back to the application context class loader");
            parentClassLoader = beanFactory.getBeanClassLoader();
        }
        final DataAccessSession session = beanFactory.getBean(DataAccessSession.class);
        final Collection<SessionInitializationEventHandler> eventHandlers = beanFactory
                .getBeansOfType(SessionInitializationEventHandler.class, false, true).values();
        for (SessionInitializationEventHandler eventHandler : eventHandlers) {
            if (eventHandler instanceof DataAccessSessionAware) {
                DataAccessSessionAware aware = (DataAccessSessionAware) eventHandler;
                aware.setDataAccessSession(session);
            }
        }
        prepareResolvers(eventHandlers);
        registerEntities(eventHandlers);
        registerExtensions(eventHandlers);
        prepareStatements(eventHandlers);
        registerInterfaces(eventHandlers);
        initializeSession(session, beanFactory, eventHandlers);
        handleClassLoader(beanFactory);
    }

}