/* * Copyright 2015 herd contributors * * 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.finra.herd.dao.config; import java.util.Properties; import javax.sql.DataSource; import com.amazonaws.retry.RetryPolicy.BackoffStrategy; import net.sf.ehcache.config.CacheConfiguration; import org.apache.commons.configuration.ConfigurationConverter; import org.apache.commons.configuration.DatabaseConfiguration; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleCacheErrorHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.ldap.core.LdapTemplate; import org.springframework.ldap.core.support.LdapContextSource; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.finra.herd.core.ApplicationContextHolder; import org.finra.herd.core.helper.ConfigurationHelper; import org.finra.herd.dao.CacheKeyGenerator; import org.finra.herd.dao.ReloadablePropertySource; import org.finra.herd.dao.SimpleExponentialBackoffStrategy; import org.finra.herd.model.dto.ConfigurationValue; import org.finra.herd.model.jpa.ConfigurationEntity; /** * DAO Spring module configuration. */ @Configuration // Component scan all packages, but exclude the configuration ones since they are explicitly specified. @ComponentScan(value = "org.finra.herd.dao", excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org\\.finra\\.herd\\.dao\\.config\\..*")) @EnableTransactionManagement @EnableCaching public class DaoSpringModuleConfig implements CachingConfigurer { private static final Logger LOGGER = LoggerFactory.getLogger(DaoSpringModuleConfig.class); @Autowired private ConfigurationHelper configurationHelper; /** * The herd cache name. */ public static final String HERD_CACHE_NAME = "herd_cache"; /** * The herd data source bean name. */ public static final String HERD_DATA_SOURCE_BEAN_NAME = "herdDataSource"; /** * The herd transaction manager bean name. */ public static final String HERD_TRANSACTION_MANAGER_BEAN_NAME = "herdTransactionManager"; /** * The Hibernate HBM2DDL Auto param bean name. */ public static final String HIBERNATE_HBM2DDL_AUTO_PARAM_BEAN_NAME = "hibernateHbm2DdlAutoParam"; /** * Model packages to scan by entity manager. */ public static final String MODEL_PACKAGES_TO_SCAN = "org.finra.herd.model.jpa"; /** * The transport client cache name. */ public static final String TRANSPORT_CLIENT_CACHE_NAME = "transport_client_cache"; /** * The JPA entity manager factory. * * @return the entity manager factory. */ @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { // Create the entity manager factory against our data source. LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean(); entityManagerFactory.setDataSource(getHerdDataSource()); // Auto-scan our model classes for persistent objects. entityManagerFactory.setPackagesToScan(MODEL_PACKAGES_TO_SCAN); // Set the JPA vendor adapter using a configured Spring bean. entityManagerFactory.setJpaVendorAdapter(getHibernateJpaVendorAdapter()); // Set JPA additional properties. entityManagerFactory.setJpaProperties(jpaProperties()); return entityManagerFactory; } /** * Gets the Hibernate JPA vendor adapter needed by the entity manager. * * @return the Hibernate JPA vendor adapter. */ private JpaVendorAdapter getHibernateJpaVendorAdapter() { HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter(); // Set the database type. String databaseType = configurationHelper.getProperty(ConfigurationValue.DATABASE_TYPE); if (StringUtils.isBlank(databaseType)) { throw new IllegalStateException( String.format("No database type found. Ensure the \"%s\" configuration entry is configured.", ConfigurationValue.DATABASE_TYPE.getKey())); } Database database = Database.valueOf(databaseType); LOGGER.info("jpaTargetDatabase={}", database); hibernateJpaVendorAdapter.setDatabase(database); hibernateJpaVendorAdapter.setGenerateDdl(false); return hibernateJpaVendorAdapter; } /** * Gets the JPA properties that contain our Hibernate specific configuration parameters. * * @return the JPA properties. */ private Properties jpaProperties() { // Create and return JPA properties. Properties properties = new Properties(); // Set the Hibernate dialect. String hibernateDialect = configurationHelper.getProperty(ConfigurationValue.HIBERNATE_DIALECT); if (StringUtils.isBlank(hibernateDialect)) { throw new IllegalStateException(String .format("No hibernate dialect found. Ensure the \"%s\" configuration entry is configured.", ConfigurationValue.HIBERNATE_DIALECT.getKey())); } properties.setProperty(ConfigurationValue.HIBERNATE_DIALECT.getKey(), hibernateDialect); LOGGER.info("hibernateDialect={}", properties.getProperty(ConfigurationValue.HIBERNATE_DIALECT.getKey())); properties.setProperty("hibernate.query.substitutions", "true='Y', false='N', yes='Y', no='N'"); properties.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory"); properties.setProperty("hibernate.cache.use_query_cache", "true"); properties.setProperty("hibernate.cache.use_second_level_cache", "true"); // Set the "show sql" flag. properties.setProperty(ConfigurationValue.SHOW_SQL.getKey(), configurationHelper.getProperty(ConfigurationValue.SHOW_SQL)); LOGGER.info("hibernateShowSql={}", properties.getProperty(ConfigurationValue.SHOW_SQL.getKey())); properties.setProperty("hibernate.archive.autodetection", "class, hbm"); // Set the Hibernate HBM2DDL Auto param if it is configured. This is only needed in JUnits. String hibernateHbm2DdlAutoParam = getHibernateHbm2DdlAutoParam(); if (StringUtils.isNotBlank(hibernateHbm2DdlAutoParam)) { properties.setProperty("hibernate.hbm2ddl.auto", hibernateHbm2DdlAutoParam); } return properties; } /** * Our Spring JPA transaction manager that will manage the JPA transactions. * * @return the JPA transaction manager. */ @Bean public JpaTransactionManager herdTransactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setDataSource(getHerdDataSource()); transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); return transactionManager; } /** * The database supplied property sources placeholder configurer that allows access to externalized properties from a database. This method also adds a new * property source that contains the database properties to the environment. * * @return the property sources placeholder configurer. */ @Bean public static PropertySourcesPlaceholderConfigurer databasePropertySourcesPlaceholderConfigurer() { // Get the configurable environment and add a new property source to it that contains the database properties. // That way, the properties can be accessed via the environment or via an injected @Value annotation. // We are adding this property source last so other property sources (e.g. system properties, environment variables) can be used // to override the database properties. Environment environment = ApplicationContextHolder.getApplicationContext().getEnvironment(); if (environment instanceof ConfigurableEnvironment) { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; ReloadablePropertySource reloadablePropertySource = new ReloadablePropertySource(ReloadablePropertySource.class.getName(), ConfigurationConverter.getProperties(getPropertyDatabaseConfiguration()), getPropertyDatabaseConfiguration()); configurableEnvironment.getPropertySources().addLast(reloadablePropertySource); } return new PropertySourcesPlaceholderConfigurer(); } /** * Gets a database configuration that can be used to read database properties. * * @return the property database configuration. */ private static DatabaseConfiguration getPropertyDatabaseConfiguration() { return new DatabaseConfiguration(getHerdDataSource(), ConfigurationEntity.TABLE_NAME, ConfigurationEntity.COLUMN_KEY, ConfigurationEntity.COLUMN_VALUE); } /** * Gets the data source bean from the application context statically. This is needed for static @Bean creation methods that won't have access to an * auto-wired data source. * * @return the data source. */ public static DataSource getHerdDataSource() { return (DataSource) ApplicationContextHolder.getApplicationContext().getBean(HERD_DATA_SOURCE_BEAN_NAME); } /** * Gets the Hibernate HBM2DDL bean from the application context statically. * * @return the Hibernate HBM2DDL auto param. */ public String getHibernateHbm2DdlAutoParam() { return (String) ApplicationContextHolder.getApplicationContext().getBean(HIBERNATE_HBM2DDL_AUTO_PARAM_BEAN_NAME); } /** * Gets an EH Cache manager. * * @return the EH Cache manager. */ @Bean(destroyMethod = "shutdown") public net.sf.ehcache.CacheManager ehCacheManager() { CacheConfiguration cacheConfiguration = new CacheConfiguration(); cacheConfiguration.setName(HERD_CACHE_NAME); cacheConfiguration.setTimeToLiveSeconds(configurationHelper.getProperty(ConfigurationValue.HERD_CACHE_TIME_TO_LIVE_SECONDS, Long.class)); cacheConfiguration.setTimeToIdleSeconds(configurationHelper.getProperty(ConfigurationValue.HERD_CACHE_TIME_TO_IDLE_SECONDS, Long.class)); cacheConfiguration.setMaxElementsInMemory(configurationHelper.getProperty(ConfigurationValue.HERD_CACHE_MAX_ELEMENTS_IN_MEMORY, Integer.class)); cacheConfiguration.setMemoryStoreEvictionPolicy(configurationHelper.getProperty(ConfigurationValue.HERD_CACHE_MEMORY_STORE_EVICTION_POLICY)); // Adding a transport client cache to store the active transport client used to connect to the search index CacheConfiguration transportClientCacheConfiguration = new CacheConfiguration(); transportClientCacheConfiguration.setName(TRANSPORT_CLIENT_CACHE_NAME); // The maximum number of seconds an element can exist in the cache regardless of use. // The element expires at this limit and will no longer be returned from the cache. // The default value is 0, which means no timeToLive (TTL) eviction takes place (infinite lifetime). transportClientCacheConfiguration .setTimeToLiveSeconds(configurationHelper.getProperty(ConfigurationValue.TRANSPORT_CLIENT_CACHE_TIME_TO_LIVE_SECONDS, Long.class)); // The maximum number of seconds an element can exist in the cache without being accessed. // The element expires at this limit and will no longer be returned from the cache. // The default value is 0, which means no timeToIdle (TTI) eviction takes place (infinite lifetime). transportClientCacheConfiguration .setTimeToIdleSeconds(configurationHelper.getProperty(ConfigurationValue.TRANSPORT_CLIENT_CACHE_TIME_TO_IDLE_SECONDS, Long.class)); // The maximum entries to be held in the cache transportClientCacheConfiguration .setMaxElementsInMemory(configurationHelper.getProperty(ConfigurationValue.TRANSPORT_CLIENT_CACHE_MAX_ELEMENTS_IN_MEMORY, Integer.class)); // The policy used to evict elements from the MemoryStore. This can be one of: // LRU - least recently used // LFU - less frequently used // FIFO - first in first out, the oldest element by creation time // The default value is LRU transportClientCacheConfiguration .setMemoryStoreEvictionPolicy(configurationHelper.getProperty(ConfigurationValue.TRANSPORT_CLIENT_CACHE_MEMORY_STORE_EVICTION_POLICY)); net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration(); config.addCache(transportClientCacheConfiguration); config.addCache(cacheConfiguration); return net.sf.ehcache.CacheManager.create(config); } @Bean @Override public CacheManager cacheManager() { return new EhCacheCacheManager(ehCacheManager()); } @Bean @Override public KeyGenerator keyGenerator() { return new CacheKeyGenerator(); } /* * No need to have a cache resolver, this is optional as we are defining cacheManager. */ @Bean @Override public CacheResolver cacheResolver() { return null; } @Bean @Override public CacheErrorHandler errorHandler() { return new SimpleCacheErrorHandler(); } @Bean public BackoffStrategy backoffStrategy() { return new SimpleExponentialBackoffStrategy(); } /** * Gets an LDAP context source. * * @return the LDAP context source */ @Bean public LdapContextSource contextSource() { String ldapUrl = configurationHelper.getProperty(ConfigurationValue.LDAP_URL); String ldapBase = configurationHelper.getProperty(ConfigurationValue.LDAP_BASE); String ldapUserDn = configurationHelper.getProperty(ConfigurationValue.LDAP_USER_DN); LOGGER.info("Creating LDAP context source using the following parameters: {}=\"{}\" {}=\"{}\" {}=\"{}\" ...", ConfigurationValue.LDAP_URL.getKey(), ldapUrl, ConfigurationValue.LDAP_BASE.getKey(), ldapBase, ConfigurationValue.LDAP_USER_DN.getKey(), ldapUserDn); LdapContextSource contextSource = new LdapContextSource(); contextSource.setUrl(ldapUrl); contextSource.setBase(ldapBase); contextSource.setUserDn(ldapUserDn); contextSource.setPassword(configurationHelper.getProperty(ConfigurationValue.LDAP_PASSWORD)); return contextSource; } /** * Gets an LDAP template. * * @return the LDAP template */ @Bean public LdapTemplate ldapTemplate() { return new LdapTemplate(contextSource()); } }