Programmatic RedissonClient in Spring boot project - spring-boot

I am trying to implement Hibernate second level caching in a Spring boot project using Redisson.
I have followed this blog as a reference
https://pavankjadda.medium.com/implement-hibernate-2nd-level-cache-with-redis-spring-boot-and-spring-data-jpa-7cdbf5632883
Also i am trying to initialize the RedissionClient programmatically and not through declaratively /through a config file
Created a spring bean to be initialized which should create the RedissonClient instance.
#Configuration
#Lazy(value = false)
public class RedissonConfig {
#Bean
public RedissonClient redissionClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
However this bean is never intialized and i get the following error while application startup.
Caused by: org.hibernate.cache.CacheException: Unable to locate Redisson configuration
at org.redisson.hibernate.RedissonRegionFactory.createRedissonClient(RedissonRegionFactory.java:107) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
at org.redisson.hibernate.RedissonRegionFactory.prepareForUse(RedissonRegionFactory.java:83) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
It seems Spring boot Hibernate still trying to load the Redisson config through a config file.
is it possible to load the Redission config in spring boot programmatically ?
Best Regards,
Saurav

I just did exactly this, here is how:
you need a custom RegionFactory that is similar to the JndiRedissonRegionFactory but gets its RedissonClient injected somehow.
an instance of this Class, fully configured, is put into the hibernate-properties map. Hibernates internal code is flexible: if the value of hibernate.cache.region.factory_class is a string it is treated as a FQDN. If it is an instance of Class<?>, it will be instantiated. If it is an Object, it will be used.
Spring offers a rather simple way to customize hibernate properties with a bean:
#AutoConfiguration(after = RedissonAutoConfiguration.class, before = JpaAutoConfiguration.class)
#ConditionalOnProperty("spring.jpa.properties.hibernate.cache.use_second_level_cache")
public class HibernateCacheAutoConfiguration {
#Bean
public HibernatePropertiesCustomizer setRegionFactory(RedissonClient redisson) {
return hibernateProperties -> hibernateProperties.put(AvailableSettings.CACHE_REGION_FACTORY, new SpringBootRedissonRegionFactory(redisson));
}
}
My RegionFactory is really simple:
#AllArgsConstructor
public class SpringBootRedissonRegionFactory extends RedissonRegionFactory {
private RedissonClient redissonClient;
#Override
protected RedissonClient createRedissonClient(Map properties) {
return redissonClient;
}
#Override
protected void releaseFromUse() {
}
}
I used the redisson-starter to get a RedissonClient, hence the reference to RedissonAutoConfiguration, but you could just create an instance by hand.

It is possible, but then you need to provide a custom implementation of RegionFactory to Hibernate, which can extends RedissonRegionFactory but uses your own client instance.

Related

Spring Boot configuration for non-beans [duplicate]

This question already has answers here:
Injecting beans into a class outside the Spring managed context
(8 answers)
Closed 3 years ago.
Introduction
I have some business logic properties in the application.yml file.
They are loaded into the application via a #ConfigurationProperties annotated class.
How could I use these properties in a class which is not a Spring Bean? It cannot be a singleton, because many objects of it must be created during run-time.
Example
application.yml
business.foo: 2
BusinessProperties.java
#ConfigurationProperties("business")
#Getter // lombok
#Setter // lombok
public class BusinessProperties {
private int foo;
}
TypicalBean.java
#Component
public class TypicalBean {
private final BusinessProperties properties;
#Autowired
public TypicalBean(BusinessProperties properties) {
this.properties = properties;
}
#PostConstruct
public void printFoo() {
System.out.println("Foo: " + properties.getFoo()); // "Foo: 2"
}
}
NonBean.java
public class NonBean {
public void printFoo() {
System.out.println("Foo: ???"); // How to access the property?
}
}
Is there some way to create a non-singleton class, which can have access to configuration (or even other Spring beans) but otherwise works the same as a regular java class? Meaning that I can control its creation, it is collected by the garbage collector if not used anymore, etc.
You can still define the NonBean.class as a Component with Scope.Prototype
#Component
#Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class NonBean {
#Autowired
public TypicalBean(BusinessProperties properties) {
this.properties = properties;
}
public void printFoo() {
System.out.println("Foo: " + properties.getFoo());
}
}
The trick is how you create an instance of NonBean.class. In the code where you'll be creating an instance of NonBean.class, use Spring's ObjectFactory<T>
private final ObjectFactory<NonBean> nonBeanFactory;
...
NonBean nonBean = nonBeanFactory.getObject();
The instantiated nonBean object will have been autowired.
All spring-beans creates by SpringApplicationContext. Bean - it's simple POJO-object, but created by Spring and saved in his container. If you want to get access to bean from outside of container - see this:
Getting Spring Application Context
Spring beans are really meant to be used within the application context but you might be able to achieve what you want by autowiring the properties to a static field in a Spring bean.
#Component
public class BusinessPropertiesUtils {
public static BusinessProperties INSTANCE;
#Autowired
public setBusinessProperties(BusinessProperties properties) {
this.INSTANCE = properties;
}
}
And then:
public class NonBean {
public void printFoo() {
System.out.println("Foo: " + BusinessPropertiesUtils.INSTANCE.getFoo());
}
}
PS: this is very hacky and definitely not the "Spring way".
You can configure beans with the prototype scope, which will give you a new instance of the bean every time it's requested.
From the Spring documentation:
In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean. The container instantiates, configures, and otherwise assembles a prototype object and hands it to the client, with no further record of that prototype instance.
...
In some respects, the Spring container’s role in regard to a prototype-scoped bean is a replacement for the Java new operator. All lifecycle management past that point must be handled by the client.
Example of how you can convert the TypicalBean class to a prototype scoped bean:
#Component
#Scope("prototype")
public class TypicalBean {
...
}
Another alternative is to manually instantiate the bean class (or any POJO) and injecting the dependencies (configuration, spring beans, etc.) through the constructor or setter methods, if you have them available or can get them from the Spring Context.
new TypicalBean(properties);

How to make globally shared objects available to freemarker templates in Spring Boot 2

What is the best way of making global shared objects available to freemarker templates when using Spring Boot 2.x, without losing Spring Boot's FreeMarker auto configuration?
The underlying mechanism for doing this is Spring Boot's FreeMakerConfigurer.setFreemarkerVariables, which in turn calls FreeMarker's Configuration.setAllSharedVariables
However, there is no obvious way (to me) to modify the FreeMarkerConfigurer that is setup by FreeMarkerServletWebConfiguration beyond the predefined freemarker properties that Spring Boot supports. (Search for "freemarker" here).
A common approach is to create a custom FreemarkerConfigurer bean, but I believe that then loses some of the auto configuration provided by spring boot, especially around the handling of various external properties.
One option that seems to work is to use a BeanPostProcessor like this:
public class CustomFreeMarkerConfig implements BeanPostProcessor {
Object sharedWithAllFreeMarkerTemplatesObj = new Object();
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof FreeMarkerConfigurer) {
FreeMarkerConfigurer configurer = (FreeMarkerConfigurer) bean;
Map<String, Object> sharedVariables = new HashMap<>();
sharedVariables.put("obj", sharedWithAllFreeMarkerTemplatesObj);
configurer.setFreemarkerVariables(sharedVariables);
}
return bean;
}
}
It seems like there should be a cleaner way of doing it, perhaps by somehow extending or configuring FreeMarkerConfigurationFactory, but I haven't been able to find it.
I found a solution from spring git
Spring Boot 2.0 breaks the solution provided by #wo8335224, as FreeMarkerWebConfiguration is replaced by FreeMarkerServletWebConfiguration, which is unfortunately package-private and thus cannot be subclassed.
A currently working solution is to configure freemarker.template.Configuration bean:
#Configuration
public class FreemarkerConfig {
public FreemarkerConfig(freemarker.template.Configuration configuration) throws TemplateModelException {
configuration.setSharedVariable("name", "whatever type of value");
}
}
Internally FreeMarkerConfigurer#setFreemarkerVariables delegates its work to freemarker.template.Configuration#setAllSharedVariables.

JAX-RS, Spring & ServletConfig: How to access Servlet Config in Configurator

I have troubles getting a javax.servlet.ServletConfig into a class annotated with org.springframework.context.annotation.Configuration.
My team decided that we should use spring for dependency injection and I'm trying to use it to migrate one of our simple Rest services.
My constraints are:
JAX-RS: We have several REST Services implemented JAX-RS and we don't really want to change that.
Not bound to a specific implementation of JAX-RS (Jersey & RESTEasy work fine for us and we can change from one to the other without changing underlying code)
Import as few dependencies as possible from spring: at the moment I import only org.springframework:spring-context from the spring project.
No API breakage: Deprecated is fine but the service should keep working during the transition, using our old way of doing things.
A string parameter is defined in the service's web.xml. I need to get it, instantiate a Bean with it and inject the resulting bean at several place in the code.
I don't want to mess with Spring Boot/MVC/... as the service already works and I just want the Dependency Injection part.
What I already have:
The code use javax.ws.rs.core.Application, with a class that look like that:
public class MyApplication extends Application {
#Context
private ServletConfig cfg;
public DSApplication() {
}
#Override
public Set<Class<?>> getClasses() {
return new HashSet<>();
}
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<>();
String injectionStr = cfg.getInitParameter("injection");
boolean injection = false;
if (null != injectionStr && !injectionStr.isEmpty()) {
injection = Boolean.valueOf(injectionStr);
}
if (injection) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
DSServiceProducer.class,
CContextBeanProvider.class
);
IDSService service = context.getBean(IDSService.class);
set.add(service);
} else {
set.add(new DSService()); //Old way
}
return set;
}
}
I need the servlet config in CContextBeanProvider, which look like:
#Configuration
public class CContextBeanProvider {
private ServletConfig cfg; // How to get this here ?
#Bean
public CContextBean cContextBean() {
String bean = cfg.getInitParameter("cpuContext");
return new CContextBean(bean);
}
}
CContextBean is a setting bean initialized from a string found in the web.xml of the service.
Is it possible ?
Do you have any idea how ?
Would it be easier with CDI, knowing that we run on base Tomcat ? (I've already find this if I need to use tomcat with CDI)
Could you please try to add all jersey CDI related jars to your applications ?

How are components managed in Spring MVC and how to inject a customized component in Spring 3.2.5?

I’m considering to replace the DefaultSessionAttributeStore implementation of Spring MVC 3.2.5 with some class of my own, and I’ve known from the source code that in my 3.2.5 spring source, it’s SessionAttributesHandler which possesses a SessionAttributeStore interface reference and invokes the session store function. My question is how to replace that by DI? The SessionAttributesHandler holds a final private sessionAttributeStore reference and can only be set by the constructor:
public class SessionAttributesHandler {
...
private final SessionAttributeStore sessionAttributeStore;
...
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null.");
this.sessionAttributeStore = sessionAttributeStore;
SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
if (annotation != null) {
this.attributeNames.addAll(Arrays.asList(annotation.value()));
this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
}
for (String attributeName : this.attributeNames) {
this.knownAttributeNames.put(attributeName, Boolean.TRUE);
}
}
...
}
Are all the components of spring mvc managed in the spring DI container? How to inject my own SessionAttributeStore implementation into SessionAttributesHandler? What does the "Class handlerType" argument mean in the constructor? From source, it seems like it's the "controller" class. Since SessionAttributesHandler is invoked and held by a ModelFactory, and in ModelFactory there is no code instantiating the SessionAttributesHandler, is there any "XML" bean configuration file for the Spring MVC inner components and how to overwrite them?
If you want to provide your own implementation of a SessionAttributeStore you need to manually configure the RequestMappingHandlerAdapter and set your custom implementation on there. That will take care of using it through-out the rest of the infrastructure.
Assuming that you use java config you can do the following
#Configuration
public class MyConfiguration extend WebMvcConfigurationSupport {
#Bean
public SessionAttributeStore sessionAttributeStore() {
return new MyCustomSessionAttributeStore();
}
#Override
#Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter rmha = super.requestMappingHandlerAdapter();
rmha.setSessionAttributeStore(sessionAttributeStore());
return rmha;
}
}
If you want to do this in XML you either have to write a BeanPostProcessor which sets it on the default RequestMappingHandlerAdapter instance created by <mvc:annotation-driven /> or configure it manually and drop the namespace support.

Convert Spring bean configuration into XML configuration

i am working on BIRT reporting tool. which is need to called by spring MVC.
i got one example from spring which is here. in this example, configuration is done via bean. can anyone help me convert this configuration in to xml based configuration ?
#EnableWebMvc
#ComponentScan({ "org.eclipse.birt.spring.core","org.eclipse.birt.spring.example" })
#Configuration
public class BirtWebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/reports").setViewName("birtView");
}
#Bean
public BirtView birtView() {
BirtView bv = new BirtView();
// bv.setReportFormatRequestParameter("ReportFormat");
// bv.setReportNameRequestParameter("ReportName");
bv.setBirtEngine(this.engine().getObject());
return bv;
}
#Bean
public BeanNameViewResolver beanNameResolver() {
BeanNameViewResolver br = new BeanNameViewResolver();
return br;
}
#Bean
protected BirtEngineFactory engine() {
BirtEngineFactory factory = new BirtEngineFactory();
return factory;
}
}
I wants a similar configuration in xml file.
There's really no tool for extracting Spring annotations to Spring bean context xml file. You'll have to do it by hand, shouldn't be too hard as all the Spring annotations functionality can be duplicated into Spring context xml tags.
if you want to use spingmvc, so no need the configuration files.
my solution is that in Birt Script i call the impl java file like this :
sampleService = new Packages.com.example.warlock.service.SampleServiceImpl();
pojo = new Packages.com.example.warlock.entity.Sample();
iterator = sampleService.getSamples().iterator();
because my SampleService is a interface and SampleServiceImpl is impl java, the two java file are not config as #Bean.
At first i want to get the data from ModelMap but failed, so i skip the controller and straight to call Service, then final call the DAO to get the Data from DB

Resources