org.codehaus.griffon.runtime.mybatis.DefaultMybatisFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.griffon.runtime.mybatis.DefaultMybatisFactory.java

Source

/*
 * Copyright 2014-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codehaus.griffon.runtime.mybatis;

import griffon.core.ApplicationClassLoader;
import griffon.core.GriffonApplication;
import griffon.core.env.Metadata;
import griffon.core.injection.Injector;
import griffon.plugins.datasource.DataSourceFactory;
import griffon.plugins.datasource.DataSourceStorage;
import griffon.plugins.monitor.MBeanManager;
import griffon.plugins.mybatis.MybatisBootstrap;
import griffon.plugins.mybatis.MybatisFactory;
import griffon.plugins.mybatis.MybatisMapper;
import griffon.util.GriffonClassUtils;
import griffon.util.ServiceLoaderUtils;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.codehaus.griffon.runtime.core.storage.AbstractObjectFactory;
import org.codehaus.griffon.runtime.jmx.SqlSessionFactoryMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Named;
import javax.sql.DataSource;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import static griffon.core.GriffonExceptionHandler.sanitize;
import static griffon.util.ConfigUtils.getConfigValueAsBoolean;
import static griffon.util.GriffonNameUtils.requireNonBlank;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;

/**
 * @author Andres Almiray
 */
public class DefaultMybatisFactory extends AbstractObjectFactory<SqlSessionFactory> implements MybatisFactory {
    private static final Logger LOG = LoggerFactory.getLogger(DefaultMybatisFactory.class);
    private static final String ERROR_SESSION_FACTORY_NAME_BLANK = "Argument 'sessionFactoryName' must not be blank";
    private final Set<String> sessionFactoryNames = new LinkedHashSet<>();

    @Inject
    private DataSourceFactory dataSourceFactory;

    @Inject
    private DataSourceStorage dataSourceStorage;

    @Inject
    private ApplicationClassLoader applicationClassLoader;

    @Inject
    private Injector injector;

    @Inject
    private MBeanManager mBeanManager;

    @Inject
    private Metadata metadata;

    private final Set<Class<?>> mappers = new LinkedHashSet<>();

    @Inject
    public DefaultMybatisFactory(@Nonnull @Named("mybatis") griffon.core.Configuration configuration,
            @Nonnull GriffonApplication application) {
        super(configuration, application);
        sessionFactoryNames.add(KEY_DEFAULT);

        if (configuration.containsKey(getPluralKey())) {
            Map<String, Object> sessionFactories = (Map<String, Object>) configuration.get(getPluralKey());
            sessionFactoryNames.addAll(sessionFactories.keySet());
        }
    }

    @Nonnull
    @Override
    public Set<String> getSessionFactoryNames() {
        return sessionFactoryNames;
    }

    @Nonnull
    @Override
    public Map<String, Object> getConfigurationFor(@Nonnull String sessionFactoryName) {
        requireNonBlank(sessionFactoryName, ERROR_SESSION_FACTORY_NAME_BLANK);
        return narrowConfig(sessionFactoryName);
    }

    @Nonnull
    @Override
    protected String getSingleKey() {
        return "sessionFactory";
    }

    @Nonnull
    @Override
    protected String getPluralKey() {
        return "sessionFactories";
    }

    @Nonnull
    @Override
    public SqlSessionFactory create(@Nonnull String name) {
        Map<String, Object> config = narrowConfig(name);
        event("MybatisConnectStart", asList(name, config));
        SqlSessionFactory sqlSessionFactory = createSqlSessionFactory(config, name);

        if (getConfigValueAsBoolean(config, "jmx", true)) {
            sqlSessionFactory = new JMXAwareSqlSessionFactory(sqlSessionFactory);
            registerMBeans(name, (JMXAwareSqlSessionFactory) sqlSessionFactory);
        }

        try (SqlSession session = openSession(name, sqlSessionFactory)) {
            for (Object o : injector.getInstances(MybatisBootstrap.class)) {
                ((MybatisBootstrap) o).init(name, session);
            }
        }

        event("MybatisConnectEnd", asList(name, config, sqlSessionFactory));
        return sqlSessionFactory;
    }

    @Override
    public void destroy(@Nonnull String name, @Nonnull SqlSessionFactory instance) {
        requireNonNull(instance, "Argument 'instance' must not be null");
        Map<String, Object> config = narrowConfig(name);
        event("MybatisDisconnectStart", asList(name, config, instance));

        try (SqlSession session = openSession(name, instance)) {
            for (Object o : injector.getInstances(MybatisBootstrap.class)) {
                ((MybatisBootstrap) o).destroy(name, session);
            }
        }

        closeDataSource(name);

        if (getConfigValueAsBoolean(config, "jmx", true)) {
            ((JMXAwareSqlSessionFactory) instance).disposeMBeans();
        }

        event("MybatisDisconnectEnd", asList(name, config));
    }

    private void registerMBeans(@Nonnull String name, @Nonnull JMXAwareSqlSessionFactory sqlSessionFactory) {
        RecordingSqlSessionFactory recordingSqlSessionFactory = (RecordingSqlSessionFactory) sqlSessionFactory
                .getDelegate();
        SqlSessionFactoryMonitor sqlSessionFactoryMonitor = new SqlSessionFactoryMonitor(metadata,
                recordingSqlSessionFactory, name);
        sqlSessionFactory
                .addObjectName(mBeanManager.registerMBean(sqlSessionFactoryMonitor, false).getCanonicalName());
    }

    @Nonnull
    @SuppressWarnings("ConstantConditions")
    private SqlSessionFactory createSqlSessionFactory(@Nonnull Map<String, Object> config,
            @Nonnull String dataSourceName) {
        DataSource dataSource = getDataSource(dataSourceName);
        Environment environment = new Environment(dataSourceName, new JdbcTransactionFactory(), dataSource);
        Configuration configuration = new Configuration(environment);

        Map<String, Object> copyOfConfig = new LinkedHashMap<>(config);
        copyOfConfig.remove("connect_on_startup");
        GriffonClassUtils.setProperties(configuration, copyOfConfig);

        if (mappers.isEmpty()) {
            readMappers();
        }

        for (Class<?> mapper : mappers) {
            configuration.addMapper(mapper);
        }

        return new RecordingSqlSessionFactory(new SqlSessionFactoryBuilder().build(configuration));
    }

    private void closeDataSource(@Nonnull String dataSourceName) {
        DataSource dataSource = dataSourceStorage.get(dataSourceName);
        if (dataSource != null) {
            dataSourceFactory.destroy(dataSourceName, dataSource);
            dataSourceStorage.remove(dataSourceName);
        }
    }

    @Nonnull
    private DataSource getDataSource(@Nonnull String dataSourceName) {
        DataSource dataSource = dataSourceStorage.get(dataSourceName);
        if (dataSource == null) {
            dataSource = dataSourceFactory.create(dataSourceName);
            dataSourceStorage.set(dataSourceName, dataSource);
        }
        return dataSource;
    }

    @Nonnull
    protected SqlSession openSession(@Nonnull String sessionFactoryName,
            @Nonnull SqlSessionFactory sqlSessionFactory) {
        return sqlSessionFactory.openSession(true);
    }

    private void readMappers() {
        ServiceLoaderUtils.load(applicationClassLoader.get(), "META-INF/types/", MybatisMapper.class,
                new ServiceLoaderUtils.LineProcessor() {
                    @Override
                    public void process(@Nonnull ClassLoader classLoader, @Nonnull Class<?> type,
                            @Nonnull String line) {
                        try {
                            Class<?> mapperClass = loadClass(line.trim(), classLoader);
                            LOG.debug("Registering {}as mybatis mapper class", mapperClass.getName());
                            mappers.add(mapperClass);
                        } catch (Exception e) {
                            if (LOG.isWarnEnabled()) {
                                LOG.warn("Could not load " + type.getName() + " with " + line, sanitize(e));
                            }
                        }
                    }
                });
    }

    protected Class<?> loadClass(@Nonnull String className, @Nonnull ClassLoader classLoader)
            throws ClassNotFoundException {
        ClassNotFoundException cnfe;

        ClassLoader cl = DefaultMybatisFactory.class.getClassLoader();
        try {
            return cl.loadClass(className);
        } catch (ClassNotFoundException e) {
            cnfe = e;
        }

        cl = classLoader;
        try {
            return cl.loadClass(className);
        } catch (ClassNotFoundException e) {
            cnfe = e;
        }

        throw cnfe;
    }
}