com.google.code.guice.repository.spi.JpaRepositoryProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.google.code.guice.repository.spi.JpaRepositoryProvider.java

Source

/*
 * Copyright (C) 2012 the original author or authors.
 * See the notice.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * 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 com.google.code.guice.repository.spi;

import com.google.code.guice.repository.configuration.JpaRepositoryModule;
import com.google.code.guice.repository.configuration.PersistenceUnitConfiguration;
import com.google.code.guice.repository.configuration.PersistenceUnitsConfigurationManager;
import com.google.code.guice.repository.configuration.RepositoryBinding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import net.jcip.annotations.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.Repository;

import javax.persistence.EntityManager;

/**
 * Provides an instance of JpaRepository. Used in {@link JpaRepositoryModule#configure()}
 *
 * @author Alexey Krylov
 * @since 10.04.2012
 */
@ThreadSafe
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
public class JpaRepositoryProvider<R extends Repository> implements Provider<R> {

    /*===========================================[ STATIC VARIABLES ]=============*/

    private static final Logger logger = LoggerFactory.getLogger(JpaRepositoryProvider.class);

    /*===========================================[ INSTANCE VARIABLES ]===========*/

    private RepositoryBinding binding;
    private String persistenceUnitName;

    private Class<R> repositoryClass;
    private Class customImplementationClass;

    private PersistenceUnitsConfigurationManager configurationManager;
    private Injector injector;
    private Class domainClass;
    private BeanFactory context;

    private volatile R repository;

    /*===========================================[ CONSTRUCTORS ]=================*/

    /**
     * Default provider constructor. Used in {@link JpaRepositoryModule#configure()}.
     *
     * @param binding             repository binding parameters container
     * @param persistenceUnitName persistence unit
     *
     * @throws IllegalArgumentException if domain class can't be resolved for specified repository in {@code binding}
     */
    public JpaRepositoryProvider(RepositoryBinding binding, String persistenceUnitName) {
        this.binding = binding;
        this.persistenceUnitName = persistenceUnitName;
        repositoryClass = binding.getRepositoryClass();
        customImplementationClass = binding.getCustomRepositoryClass();
        domainClass = TypeUtil.getFirstTypeParameterClass(repositoryClass);

        if (domainClass == null) {
            throw new IllegalArgumentException(
                    String.format("Unable to resolve domain class for repository [%s]", repositoryClass.getName()));
        }
    }

    @Inject
    public void init(Injector injector, ApplicationContext context,
            CustomRepositoryImplementationResolver implementationResolver,
            PersistenceUnitsConfigurationManager configurationManager) {
        this.injector = injector;
        this.context = context;

        if (customImplementationClass == null) {
            customImplementationClass = implementationResolver.resolve(repositoryClass);
        }

        if (customImplementationClass != null) {
            logger.info(String.format("Custom repository implementation class for [%s] set to [%s]",
                    repositoryClass.getName(), customImplementationClass.getName()));
        }
        this.configurationManager = configurationManager;
    }

    /*===========================================[ INTERFACE METHODS ]============*/

    @Override
    public R get() {
        // double-checked locking with volatile
        R repo = repository;
        if (repo == null) {
            synchronized (this) {
                repo = repository;
                if (repo == null) {
                    JpaRepositoryFactoryBean jpaRepositoryFactoryBean = createJpaRepositoryFactoryBean();
                    PersistenceUnitConfiguration configuration = configurationManager
                            .getConfiguration(persistenceUnitName);

                    // Repository will hold a link to Configuration and if underlying EM will be changed - repo instance will get it
                    EntityManager entityManager = configuration.asEntityManagerProxy();
                    // Needs to be set first
                    jpaRepositoryFactoryBean.setTransactionManager(configuration.getTransactionManagerName());
                    // Attaching to Spring's context
                    jpaRepositoryFactoryBean.setBeanFactory(context);
                    jpaRepositoryFactoryBean.setEntityManager(entityManager);
                    jpaRepositoryFactoryBean.setRepositoryInterface(repositoryClass);
                    jpaRepositoryFactoryBean.setNamedQueries(binding.getNamedQueries());
                    jpaRepositoryFactoryBean.setQueryLookupStrategyKey(binding.getQueryLookupStrategyKey());

                    if (customImplementationClass != null) {
                        Object customRepositoryImplementation = instantiateCustomRepository(entityManager);

                        if (customRepositoryImplementation != null) {
                            jpaRepositoryFactoryBean.setCustomImplementation(customRepositoryImplementation);
                        }
                    }

                    jpaRepositoryFactoryBean.afterPropertiesSet();
                    repository = (R) jpaRepositoryFactoryBean.getObject();

                    repo = repository;
                }
            }
        }
        return repo;
    }

    /**
     * Some instantiation example is <a href="https://jira.springsource.org/browse/DATAJPA-69">here</a>.
     */
    protected JpaRepositoryFactoryBean createJpaRepositoryFactoryBean() {
        return injector.getInstance(CustomJpaRepositoryFactoryBean.class);
    }

    protected Object instantiateCustomRepository(EntityManager entityManager) {
        Object customRepositoryImplementation = null;
        /**
         * Watching a case when Repository implementation is a SimpleJpaRepository subclass -
         * we need to call a constructor with some parameters.
         */
        try {
            if (SimpleJpaRepository.class.isAssignableFrom(customImplementationClass)) {
                customRepositoryImplementation = customImplementationClass
                        .getConstructor(Class.class, EntityManager.class).newInstance(domainClass, entityManager);
                injector.injectMembers(customRepositoryImplementation);
            } else {
                customRepositoryImplementation = injector.getInstance(customImplementationClass);
            }
        } catch (Exception e) {
            logger.error(String.format(
                    "Unable to instantiate custom repository implementation. Repository class is [%s]",
                    customImplementationClass.getName()), e);
        }
        return customRepositoryImplementation;
    }
}