package fr.openwide.core.jpa.config.spring;
import static com.google.common.base.Strings.emptyToNull;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.persistence.SharedCacheMode;
import javax.persistence.spi.PersistenceProvider;
import javax.sql.DataSource;
import org.apache.lucene.analysis.Analyzer;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.cache.ehcache.EhCacheRegionFactory;
import org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory;
import org.hibernate.cfg.Environment;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.search.store.impl.FSDirectoryProvider;
import org.hibernate.search.store.impl.RAMDirectoryProvider;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.util.StringUtils;
import com.google.common.collect.Lists;
import com.zaxxer.hikari.HikariDataSource;
import fr.openwide.core.jpa.business.generic.service.ITransactionalAspectAwareService;
import fr.openwide.core.jpa.config.spring.provider.IDatabaseConnectionPoolConfigurationProvider;
import fr.openwide.core.jpa.config.spring.provider.IJpaConfigurationProvider;
import fr.openwide.core.jpa.config.spring.provider.IJpaPropertiesProvider;
import fr.openwide.core.jpa.config.spring.provider.JpaPackageScanProvider;
import fr.openwide.core.jpa.exception.ServiceException;
import fr.openwide.core.jpa.hibernate.model.naming.PostgreSQLPhysicalNamingStrategyImpl;
public final class JpaConfigUtils {
private JpaConfigUtils() {}
/**
* Construit un {@link LocalContainerEntityManagerFactoryBean} à partir d'un ensemble d'options usuelles.
*/
public static LocalContainerEntityManagerFactoryBean entityManagerFactory(IJpaConfigurationProvider provider) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setJpaProperties(getJpaProperties(provider));
entityManagerFactoryBean.setDataSource(provider.getDataSource());
entityManagerFactoryBean.setPackagesToScan(getPackagesToScan(provider.getJpaPackageScanProviders()));
PersistenceProvider persistenceProvider = provider.getPersistenceProvider();
if (persistenceProvider != null) {
entityManagerFactoryBean.setPersistenceProvider(persistenceProvider);
}
return entityManagerFactoryBean;
}
public static Properties getJpaProperties(IJpaPropertiesProvider configuration) {
Properties properties = new Properties();
properties.setProperty(Environment.DIALECT, configuration.getDialect().getName());
properties.setProperty(Environment.HBM2DDL_AUTO, configuration.getHbm2Ddl());
properties.setProperty(Environment.SHOW_SQL, Boolean.FALSE.toString());
properties.setProperty(Environment.FORMAT_SQL, Boolean.FALSE.toString());
properties.setProperty(Environment.GENERATE_STATISTICS, Boolean.FALSE.toString());
properties.setProperty(Environment.USE_REFLECTION_OPTIMIZER, Boolean.TRUE.toString());
properties.setProperty(Environment.CREATE_EMPTY_COMPOSITES_ENABLED,
Boolean.valueOf(configuration.isCreateEmptyCompositesEnabled()).toString());
Integer defaultBatchSize = configuration.getDefaultBatchSize();
if (defaultBatchSize != null) {
properties.setProperty(Environment.DEFAULT_BATCH_FETCH_SIZE, Integer.toString(defaultBatchSize));
properties.setProperty(Environment.BATCH_FETCH_STYLE, BatchFetchStyle.PADDED.name());
}
String hibernateHbm2DdlImportFiles = configuration.getHbm2DdlImportFiles();
if (StringUtils.hasText(hibernateHbm2DdlImportFiles)) {
properties.setProperty(Environment.HBM2DDL_IMPORT_FILES, hibernateHbm2DdlImportFiles);
}
String ehCacheConfiguration = configuration.getEhCacheConfiguration();
boolean singletonCache = configuration.isEhCacheSingleton();
boolean queryCacheEnabled = configuration.isQueryCacheEnabled();
if (StringUtils.hasText(ehCacheConfiguration)) {
if (configuration.getEhCacheRegionFactory() != null) {
properties.setProperty(Environment.CACHE_REGION_FACTORY, configuration.getEhCacheRegionFactory().getName());
} else {
if (singletonCache) {
properties.setProperty(Environment.CACHE_REGION_FACTORY, SingletonEhCacheRegionFactory.class.getName());
} else {
properties.setProperty(Environment.CACHE_REGION_FACTORY, EhCacheRegionFactory.class.getName());
}
}
properties.setProperty(AvailableSettings.SHARED_CACHE_MODE, SharedCacheMode.ENABLE_SELECTIVE.name());
properties.setProperty(EhCacheRegionFactory.NET_SF_EHCACHE_CONFIGURATION_RESOURCE_NAME, ehCacheConfiguration);
properties.setProperty(Environment.USE_SECOND_LEVEL_CACHE, Boolean.TRUE.toString());
if (queryCacheEnabled) {
properties.setProperty(Environment.USE_QUERY_CACHE, Boolean.TRUE.toString());
} else {
properties.setProperty(Environment.USE_QUERY_CACHE, Boolean.FALSE.toString());
}
} else {
if (queryCacheEnabled) {
throw new IllegalArgumentException("Could not enable query cache without EhCache configuration");
}
properties.setProperty(Environment.USE_SECOND_LEVEL_CACHE, Boolean.FALSE.toString());
properties.setProperty(Environment.USE_QUERY_CACHE, Boolean.FALSE.toString());
}
String hibernateSearchIndexBase = configuration.getHibernateSearchIndexBase();
if (StringUtils.hasText(hibernateSearchIndexBase)) {
if (configuration.isHibernateSearchIndexInRam()) {
properties.setProperty("hibernate.search.default.directory_provider", RAMDirectoryProvider.class.getName());
} else {
properties.setProperty("hibernate.search.default.directory_provider", FSDirectoryProvider.class.getName());
properties.setProperty("hibernate.search.default.locking_strategy", "native");
}
properties.setProperty("hibernate.search.default.indexBase", hibernateSearchIndexBase);
properties.setProperty("hibernate.search.default.exclusive_index_use", Boolean.TRUE.toString());
properties.setProperty(org.hibernate.search.cfg.Environment.LUCENE_MATCH_VERSION,
org.hibernate.search.cfg.Environment.DEFAULT_LUCENE_MATCH_VERSION.toString());
} else {
properties.setProperty("hibernate.search.autoregister_listeners", Boolean.FALSE.toString());
}
Class<? extends Analyzer> hibernateSearchDefaultAnalyzer = configuration.getHibernateSearchDefaultAnalyzer();
if (hibernateSearchDefaultAnalyzer != null) {
properties.setProperty(org.hibernate.search.cfg.Environment.ANALYZER_CLASS, hibernateSearchDefaultAnalyzer.getName());
}
String hibernateSearchIndexingStrategy = configuration.getHibernateSearchIndexingStrategy();
if (StringUtils.hasText(hibernateSearchIndexingStrategy)) {
properties.setProperty(org.hibernate.search.cfg.Environment.INDEXING_STRATEGY, hibernateSearchIndexingStrategy);
}
String validationMode = configuration.getValidationMode();
if (StringUtils.hasText(validationMode)) {
properties.setProperty(AvailableSettings.VALIDATION_MODE, validationMode);
}
Class<? extends ImplicitNamingStrategy> implicitNamingStrategy = configuration.getImplicitNamingStrategy();
if (implicitNamingStrategy != null) {
properties.setProperty(Environment.IMPLICIT_NAMING_STRATEGY, implicitNamingStrategy.getName());
} else {
throw new IllegalStateException(Environment.IMPLICIT_NAMING_STRATEGY + " may not be null: sensible values are "
+ ImplicitNamingStrategyJpaCompliantImpl.class.getName() + " for OWSI-Core <= 0.7 or "
+ ImplicitNamingStrategyComponentPathImpl.class.getName() + " for OWSI-Core >= 0.8");
}
Class<? extends PhysicalNamingStrategy> physicalNamingStrategy = configuration.getPhysicalNamingStrategy();
if (physicalNamingStrategy != null) {
properties.setProperty(Environment.PHYSICAL_NAMING_STRATEGY, physicalNamingStrategy.getName());
} else {
throw new IllegalStateException(Environment.PHYSICAL_NAMING_STRATEGY + " may not be null: sensible values are "
+ PhysicalNamingStrategyStandardImpl.class.getName() + " for no filtering of the name "
+ PostgreSQLPhysicalNamingStrategyImpl.class.getName() + " to truncate the name to conform with PostgreSQL identifier max length");
}
Boolean isNewGeneratorMappingsEnabled = configuration.isNewGeneratorMappingsEnabled();
if (isNewGeneratorMappingsEnabled != null) {
properties.setProperty(org.hibernate.cfg.AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, isNewGeneratorMappingsEnabled.toString());
}
return properties;
}
public static Advisor defaultTransactionAdvisor(PlatformTransactionManager transactionManager) {
return defaultTransactionAdvisor(transactionManager, Lists.<Class<? extends Exception>>newArrayList());
}
public static Advisor defaultTransactionAdvisor(PlatformTransactionManager transactionManager,
List<Class<? extends Exception>> additionalRollbackRuleExceptions) {
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
advisor.setExpression("this(" + ITransactionalAspectAwareService.class.getName() + ")");
advisor.setAdvice(defaultTransactionInterceptor(transactionManager, additionalRollbackRuleExceptions));
return advisor;
}
/**
* Construit un transactionInterceptor avec une configuration par défaut.
*/
public static TransactionInterceptor defaultTransactionInterceptor(PlatformTransactionManager transactionManager,
List<Class<? extends Exception>> additionalRollbackRuleExceptions) {
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
Properties transactionAttributes = new Properties();
List<RollbackRuleAttribute> rollbackRules = Lists.newArrayList();
rollbackRules.add(new RollbackRuleAttribute(ServiceException.class));
// TODO voir si on ajoute SecurityServiceException.class en fonction de ce que ça donne sur le Wombat
// ou voir si on ne la dégage pas carrément en fait...
for (Class<? extends Exception> clazz : additionalRollbackRuleExceptions) {
rollbackRules.add(new RollbackRuleAttribute(clazz));
}
DefaultTransactionAttribute readOnlyTransactionAttributes =
new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED);
readOnlyTransactionAttributes.setReadOnly(true);
RuleBasedTransactionAttribute writeTransactionAttributes =
new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, rollbackRules);
String readOnlyTransactionAttributesDefinition = readOnlyTransactionAttributes.toString();
String writeTransactionAttributesDefinition = writeTransactionAttributes.toString();
// read-only
transactionAttributes.setProperty("is*", readOnlyTransactionAttributesDefinition);
transactionAttributes.setProperty("has*", readOnlyTransactionAttributesDefinition);
transactionAttributes.setProperty("get*", readOnlyTransactionAttributesDefinition);
transactionAttributes.setProperty("list*", readOnlyTransactionAttributesDefinition);
transactionAttributes.setProperty("search*", readOnlyTransactionAttributesDefinition);
transactionAttributes.setProperty("find*", readOnlyTransactionAttributesDefinition);
transactionAttributes.setProperty("count*", readOnlyTransactionAttributesDefinition);
// write et rollback-rule
transactionAttributes.setProperty("*", writeTransactionAttributesDefinition);
transactionInterceptor.setTransactionAttributes(transactionAttributes);
transactionInterceptor.setTransactionManager(transactionManager);
return transactionInterceptor;
}
public static DataSource dataSource(IDatabaseConnectionPoolConfigurationProvider configurationProvider) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName(configurationProvider.getDriverClass().getName());
dataSource.setJdbcUrl(configurationProvider.getUrl());
dataSource.setUsername(configurationProvider.getUser());
dataSource.setPassword(configurationProvider.getPassword());
dataSource.addDataSourceProperty("user", configurationProvider.getUser());
dataSource.addDataSourceProperty("password", configurationProvider.getPassword());
dataSource.setMinimumIdle(configurationProvider.getMinIdle());
dataSource.setMaximumPoolSize(configurationProvider.getMaxPoolSize());
dataSource.setConnectionTestQuery(configurationProvider.getValidationQuery());
dataSource.setConnectionInitSql(emptyToNull(configurationProvider.getInitSql()));
return dataSource;
}
private static String[] getPackagesToScan(List<JpaPackageScanProvider> jpaPackageScanProviders) {
Set<String> packagesToScan = new HashSet<String>();
for (JpaPackageScanProvider jpaPackageScanProvider : jpaPackageScanProviders) {
for (Package packageToScan : jpaPackageScanProvider.getPackages()) {
packagesToScan.add(packageToScan.getName());
}
}
return packagesToScan.toArray(new String[packagesToScan.size()]);
}
}