Set Spring Session Redis on production only - spring-boot

Is it possible to set the Spring session data redis module on production profile only?
I have tried using #Profile but the AbstractHttpSessionApplicationInitializer was initialized before setting the profile/environment and thus will give me an NoSuchBean Exception
NoSuchBeanDefinitionException: No bean named 'springSessionRepositoryFilter' available
And when I try to inject the profile using #Values it returns null value.
#Value("${spring.profiles.active:local}")
private String activeProfile; // result is null

I have solved the problem using this article.
The production session config will be like this.
#Configuration
#ConditionalOnProperty(name = "spring.profiles.active", havingValue = "production")
#EnableRedisHttpSession
#ConfigurationProperties(prefix = "redis.db")
public class ProductionSessionConfig {
#Setter
private String server;
#Setter
private Integer port;
#Bean
public JedisConnectionFactory connectionFactory() {
//creates your own connection
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(server, port);
return new JedisConnectionFactory(config);
}
}
And if you are not going to use the spring-session-data-redis in other environment. You can just proceed. Just keep in mind the #ConditionalOnProperty value.
And do not forget to exclude the Auto Configuration of Session.
#SpringBootApplication(exclude = {SessionAutoConfiguration.class})

Related

How to enable distributed/clustered cache when using redis with spring data cache

How to enable distributed/clustered cache when using Redis with spring-boot cache.
Especially when using Redis through spring-boot-starter-data-redis
Enable caching in the spring boot app is very simple. You would need to just follow three steps.
Define cache configuration
Add EnableCaching to any configuration class
Provide a CacheManager bean
For Redis, we've RedisCacheManager that can be configured and created.
Cache Configuration
#Configuration
#Getter
#Setter
#ConfigurationProperties(prefix = "cache")
public class CacheConfigurationProperties {
// Redis host name
private String redisHost;
// Redis port
private int redisPort;
// Default TTL
private long timeoutSeconds;
// TTL per cache, add enties for each cache
private Map<String, Long> cacheTtls;
}
Set their values via properties or yaml file like
cache.redisHost=localhost
cache.redisPort=6379
cache.timeoutSeconds=1000
cache.cacheTtls.cach1=100
cache.cacheTtls.cach2=200
Once you have created configuration, you can create cache config for RedisCacheManger by builder.
#Configuration
#EnableCaching
public class CacheConfig {
private static RedisCacheConfiguration createCacheConfiguration(long timeoutInSeconds) {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(timeoutInSeconds));
}
#Bean
public LettuceConnectionFactory redisConnectionFactory(CacheConfigurationProperties properties) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(properties.getRedisHost());
redisStandaloneConfiguration.setPort(properties.getRedisPort());
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
#Bean
public RedisCacheConfiguration cacheConfiguration(CacheConfigurationProperties properties) {
return createCacheConfiguration(properties.getTimeoutSeconds());
}
#Bean
public CacheManager cacheManager(
RedisConnectionFactory redisConnectionFactory, CacheConfigurationProperties properties) {
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
for (Entry<String, Long> cacheNameAndTimeout : properties.getCacheTtls().entrySet()) {
cacheConfigurations.put(
cacheNameAndTimeout.getKey(), createCacheConfiguration(cacheNameAndTimeout.getValue()));
}
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration(properties))
.withInitialCacheConfigurations(cacheConfigurations)
.build();
}
}
If you're using Redis cluster than update cache properties as per that. In this some beans would become primary if you want cache specific bean than make these methods private.

Configuring Spring OAuth2 Client for client_credentials flow

We have the following client configuration for our oauth2 clients in place that worked quite well with spring boot 1.4.0:
#Configuration
#ConfigurationProperties(prefix = "pmc.oauth.client")
public class OAuthClientConfig {
#NotNull
private String scope;
#NotNull
private String clientSecret;
#NotNull
private String clientId;
#NotNull
private String accessTokenUri;
private int clientReadTimeout = 60000;
private int clientConnectTimeout = 60000;
#Bean
public OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails() {
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
resourceDetails.setAccessTokenUri(accessTokenUri);
resourceDetails.setClientId(clientId);
resourceDetails.setClientSecret(clientSecret);
resourceDetails.setScope(Collections.singletonList(scope));
return resourceDetails;
}
#Bean
public OAuth2ClientContext oauth2ClientContext() {
DefaultOAuth2ClientContext defaultOAuth2ClientContext = new DefaultOAuth2ClientContext();
return defaultOAuth2ClientContext;
}
#Bean
public OAuth2RestTemplate oAuth2RestTemplate(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails, OAuth2ClientContext oauth2ClientContext) {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(oAuth2ProtectedResourceDetails, oauth2ClientContext);
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
restTemplate.setRequestFactory(factory);
return restTemplate;
}
}
After updating to Spring-Boot 1.4.1 I've noticed that when autowiring the OAuth2RestTemplate another implementation of OAuth2ProtectedResourceDetails takes precedence (due to the org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ProtectedResourceDetailsConfiguration - autoconfiguration which marks a bean of AuthorizationCodeResourceDetails as primary) over our own defined bean of type ClientCredentialsResourceDetails.
I know I can fix this by not autowiring the resourceDetails and clientContext for the rest template bean and supplying the concrete implementations directly but I was just wondering whether we are configuring our rest template in a way that was not intended by the spring team?
Any thoughts on how to properly configure our rest template for the client_credentials flow?
Cheers,
Ben
I was struggling with the same issue. However, I found that if you add the annotation #EnableOAuth2Client annotation along with:
security.oauth2.client.grant-type = client_credentials
you get the proper injected ClientCredentialsResourceDetails details instance rather than the AuthorizationCodeResourceDetails. In addition, trying to exclude the OAuth2ProtectedResourceDetailsConfiguration does not work. It is a package private class. If you try to exclude it by name, you will get an error stating it is not an auto configuration class. Look at the code for OAuth2RestOperationsConfiguration class. It is what determines which resource to allocate based on inspecting properties within the security.oauth2.client prefix.
I stuck into the same issue right now. My solution is to use a named bean for my ResourceDetails bean and use #Qualifier to inject the proper one when I'm creating the rest template (in your case in the parameters of the oAuth2RestTemplate method).
It would be nice to know the exact cause of this problem with the version upgrade between 1.4.0 and 1.4.1 though.

Override application properties in Spring Boot integration tests

I'm preparing a Spring Boot starter (used in tests) and I want to override a specific application property. This particular case regards enabling / disabling cache based on a property (production-code starter uses #ConditionalOnProperty). When using the test starter I want to have the caching disabled by default.
Is there a way to do that except using #TestPropertySource? Hence it is not a repeatable annotation, I don't want to use it. It's a recommended way for end-user to add properties required for the test case.
Edit:
I'm providing more details on the specific use case
Production Starter auto-configuration:
#Configuration
#ConditionalOnWebApplication
#EnableConfigurationProperties(StarterCacheProperties.class)
#EnableCaching
public class StarterCacheAutoConfiguration {
..(omitted for clarity)..
#Configuration
#ConditionalOnProperty(prefix = "neostarter.saasmgr.cache", name = "enabled", havingValue = "false")
public static class SaasMgrNoCacheConfig extends CachingConfigurerSupport {
#Bean(name = SAAS_MGR_CACHE_MANAGER)
#Override
public CacheManager cacheManager() {
return new NoOpCacheManager();
}
}
#Configuration
#ConditionalOnProperty(prefix = "neostarter.saasmgr.cache", name = "enabled", matchIfMissing = true)
public static class SaasMgrCachingConfig extends CachingConfigurerSupport {
#Autowired
SaasMgrCacheProperties saasMgrCacheProperties;
#Bean(destroyMethod = "shutdown")
public net.sf.ehcache.CacheManager ehCacheManager() {
CacheConfiguration cacheConfiguration = new CacheConfiguration(SAAS_MGR_AUTH_CACHE, 1000)
.timeToLiveSeconds(saasMgrCacheProperties.getTimeToLiveSeconds());
net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
config.addCache(cacheConfiguration);
return net.sf.ehcache.CacheManager.newInstance(config);
}
#Bean(name = SAAS_MGR_CACHE_MANAGER)
#Override
public CacheManager cacheManager() {
return new EhCacheCacheManager(ehCacheManager());
}
}
This gives user a choice of caching particular calls in production (or not). However, I find it reasonable to disable caching in tests by default. I am fully aware that this can still be overridden by end-user. In a test starter (different dependency) I tried the following:
#Configuration
#AutoConfigureBefore({CacheAutoConfiguration.class, SaasMgrSecurityAutoConfiguration.class})
public class DisableCacheAutoConfiguration {
#Autowired
Environment environment;
#PostConstruct
public void disableCache() {
EnvironmentTestUtils.addEnvironment((ConfigurableEnvironment)environment, "neostarter.saasmgr.cache.enabled=false");
}
}
but no matter what StarterCacheAutoConfiguration with EhCacheCacheManager is always resolved before DisableCacheAutoConfiguration. In debug in AutoConfigurationSorter.java I see proper order of the configuration:
15 = "com.neoteric.starter.auth.saasmgr.test.DisableCacheAutoConfiguration"
16 = "com.neoteric.starter.auth.SaasMgrSecurityAutoConfiguration"
36 = "org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration"
You can't change user's configuration in a starter, that would be way too opaque and invasive. You have no guarantee about it anyway: anybody could set the property value at a higher level (e.g. system property) and override what you're trying to change.
If you want to disable caching, the most easiest way to do it is to do what spring.cache.type=none does, that is providing an implementation of CacheManager that does not do anything.
Try to declare a bean of type org.springframework.cache.support.NoOpCacheManager

Spring Java based config "chicken and Egg situation"

Just recently started looking into Spring and specifically its latest features, like Java config etc.
I have this somewhat strange issue:
Java config Snippet:
#Configuration
#ImportResource({"classpath*:application-context.xml","classpath:ApplicationContext_Output.xml"})
#Import(SpringJavaConfig.class)
#ComponentScan(excludeFilters={#ComponentScan.Filter(org.springframework.stereotype.Controller.class)},basePackages = " com.xx.xx.x2.beans")
public class ApplicationContextConfig extends WebMvcConfigurationSupport {
private static final Log log = LogFactory.getLog(ApplicationContextConfig.class);
#Autowired
private Environment env;
#Autowired
private IExtendedDataSourceConfig dsconfig;
#PostConstruct
public void initApp() {
...
}
#Bean(name="transactionManagerOracle")
#Lazy
public DataSourceTransactionManager transactionManagerOracle() {
return new DataSourceTransactionManager(dsconfig.oracleDataSource());
}
IExtendedDataSourceConfig has two implementations which are based on spring active profile one or the other in instantiated. For this example let say this is the implementation :
#Configuration
#PropertySources(value = {
#PropertySource("classpath:MYUI.properties")})
#Profile("dev")
public class MYDataSourceConfig implements IExtendedDataSourceConfig {
private static final Log log = LogFactory.getLog(MYDataSourceConfig.class);
#Resource
#Autowired
private Environment env;
public MYDataSourceConfig() {
log.info("creating dev datasource");
}
#Bean
public DataSource oracleDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl(env.getProperty("oracle.url"));
dataSource.setUsername(env.getProperty("oracle.user"));
dataSource.setPassword(env.getProperty("oracle.pass"));
return dataSource;
}
The problem is that when transactionManagerOracle bean is called, (even if I try to mark it as lazy) dsconfig variable value appears to be null.
I guess #beans are processed first and then all Autowires, is there a fix for this? How do I either tell spring to inject dsconfig variable before creating beans, or somehow create #beans after dsconfig is injected?
You can just specify DataSource as method parameter for the transaction manager bean. Spring will then automatically inject the datasource, which is configured in the active profile:
#Bean(name="transactionManagerOracle")
#Lazy
public DataSourceTransactionManager transactionManagerOracle(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
If you still want to do this through the configuration class, specify that as parameter:
public DataSourceTransactionManager transactionManagerOracle(IExtendedDataSourceConfig dsconfig) {}
In both ways you declare a direct dependency to another bean, and Spring will make sure, that the dependent bean exists and will be injected.

Spring Boot Data JPA: Hibernate Session issue

I'm developing a Spring Boot based web application. I heavily rely on #ComponentScan and #EnableAutoConfiguration and no explicit XML configuration in place.
I have the following problem. I have a JPA-Annotated Entity class called UserSettings:
#Entity public class UserSettings {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToMany(cascade = CascadeType.ALL)
private Set<Preference> preferences; // 'Preference' is another #Entity class
public UserSettings() {
this.preferences = new HashSet<Preference>();
}
// some more primitive properties, Getters, Setters...
}
I followed this tutorial and created a repository interface that extends JpaRepository<UserSettings,Long>.
Furthermore, I have a UserManager bean:
#Component public class SettingsManager {
#Autowired
UserSettingsRepository settingsRepository;
#PostConstruct
protected void init() {
// 'findGlobalSettings' is a simple custom HQL query
UserSettings globalSettings = this.settingsRepository.findGlobalSettings();
if (globalSettings == null) {
globalSettings = new UserSettings();
this.settingsRepository.saveAndFlush(globalSettings);
}
}
Later in the code, I load the UserSettings object created here, again with the findGlobalSetttings query.
The problem is: Every time I try to access the #OneToMany attribute of the settings object, I get the following exception:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role org.example.UserSettings.preferences, could not initialize proxy - no Session
I understand that each HTTP Session has its own Hibernate Session, as described in the accepted answer of this question, but that does not apply in my case (currently I'm testing this within the same HTTP Session), which is why I have no idea where this exception comes from.
What am I doing wrong and how can I achieve circumvent the error?
If you want to be able to access mapped entities outside the transaction (which you seem to be doing), you need to flag it as an "eager" join. i.e.
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
This question has been answered beautifully by #Steve. However, if you still want to maintain your lazy loading implementation, you may want to try this
import javax.servlet.Filter;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate4.support.OpenSessionInViewFilter;
#Configuration
#ComponentScan
public class AppConfig {
#Bean
public FilterRegistrationBean filterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(openSessionInView());
registration.addUrlPatterns("/*");
return registration;
}
#Bean
public Filter openSessionInView() {
return new OpenSessionInViewFilter();
}
}
What this configuration does is, it registers a Filter on requests to path "/*" which keeps your Hibernate Session open in your view.
This is an anti-pattern and must be used with care.
NOTE: As of Spring Boot 1.3.5.RELEASE, when you use the default configuration with Spring Data JPA auto-configuration, you shouldn't encounter this problem
I faced similar issue in spring boot application, after googling I'm able to fix this issue by adding the following code to my application.
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.enable_lazy_load_no_trans", true);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
Referred here.

Resources