Multiple RedisConnectionFactories in Spring Boot Application - spring

My application uses one "main" redis instance for things like session storage and cache but needs to talk to a separate "external" instance for other reasons. I am trying to determine the "best" ("most idiomatic"? "simplest"?) way to configure this in my Spring Boot application.
Ideally I'd just like to use the default auto-configuration for the main instance but as soon as I register a connection factory for the external instance the #ConditionalOnMissngBean({RedisConnectionFactory.class}) condition in LettuceConnectionConfiguration becomes false and so the default instance isn't created. Looking at what else is going on in LettuceConnectionConfiguration etc. I feel like I'd rather not manually configure it if I don't need to.
I could just not expose the "external" connection factory as a bean and only use it internally to create the beans that depend on it but, while that would be ok in my specific case, I'd like to understand if there's a better solution where both factories can be exposed.
Is there some way I can expose the second RedisConnectionFactory without disabling the default one provided by auto configuration? Is there a clear "right way" to do this sort of thing?

you must implement the BeanDefinitionRegistryPostProcessor to adjust the RedisConnectionFactory order
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;
#Component
public class MultipleRedisConnectionFactoryRegistrar implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition bd1 = registry.getBeanDefinition("redisConnectionFactory");
if (bd1 != null) {
BeanDefinition bd = new RootBeanDefinition(ExternalRedisConnectionFactoryBean.class);
registry.registerBeanDefinition("externalRedisConnectionFactory", bd);
}
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
in ExternalRedisConnectionFactoryBean, you can create your own RedisConnectionFactory
import org.springframework.beans.factory.FactoryBean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
public class ExternalRedisConnectionFactoryBean implements FactoryBean<RedisConnectionFactory> {
#Override
public RedisConnectionFactory getObject() throws Exception {
//you can mannually create your external redis connection factory here
return null;
}
#Override
public Class<?> getObjectType() {
return RedisConnectionFactory.class;
}
}
if you want to use the multiple RedisConnectionFactory, you #Qualifier is the right choice, for example
#Autowired
#Qualifier("redisConnectionFactory")
private RedisConnectionFactory defaultRedisConnectionFactory;
#Autowired
#Qualifier("externalRedisConnectionFactory")
private RedisConnectionFactory externalRedisConnectionFactory;

Related

How to implement multiton pattern using Spring Framework

How does one implement multiton pattern using Spring Framework's facilities?
https://en.wikipedia.org/wiki/Multiton_pattern
I want to write a factory which takes a pair of client and supplier as arguments. The factory should always return a bean of type T. For a given pair of client and supplier, the instance of T return should be a singleton, but for a different pair of client and supplier, it will be a different instance of T. Please suggest a way to implement this without implementing boilerplate code that Spring may already provide.
Interface ClientSdk {
sendRequestToClient();
}
class ClientASdk implements ClientSdk {
}
class ClientBSdk implements ClientSdk {
}
enum Client {
ClientA,
ClientB;
}
enum Supplier {
SupplierA,
SupplierB;
}
class ClientSupplier {
private Client client;
private Supplier supplier;
}
class SdkFactory {
public ClientSdk getClientSdk(ClientSupplier clientSupplier) {
//For a given ClientSupplier, always return the same
//ClientSupplier instance
}
}
#Service
class ClientRequestService {
public sendRequestToClient(ClientSupplier clientSupplier) {
ClientSdk clientSdk = SdkFactory.getSdk(clientSupplier);
clientSdk.sendRequestToClient();
}
}
Here's a solution to your problem. It does make SdkFactory a bean as #crizzis suggests, but it also creates bean instances for each ClientSdk instance so that each of them can be autowired or otherwise helped out by Spring. Note that I added an ident() method to the ClientSdk interface just to show that the MyClientSdk beans have in fact been autowired with the Spring Environment:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
interface ClientSdk {
void sendRequestToClient();
}
// This class is instantiated via a #Bean method inside SdkFactory. Since it is annotated as a Prototype bean,
// multiple instances of this class can be created as beans.
class MyClientSdk implements ClientSdk {
#Autowired
Environment environment;
private final String clientSupplier;
MyClientSdk(String clientSupplier) {
this.clientSupplier = clientSupplier;
System.out.printf("### Created MyClientSdk for: %s\n", clientSupplier);
}
public void sendRequestToClient() {
System.out.printf("Sending request for client: %s\n", clientSupplier);
System.out.printf("CS: %s Environment Prop: %s\n", clientSupplier, environment.getProperty("spring.application.name"));
}
}
#Component
class SdkFactory implements BeanFactoryAware {
private Map<String, ClientSdk> sdks = new HashMap<>();
private BeanFactory beanFactory;
// Here's the key logic to insure that we get just one instance of ClientSdk per clientSupplier value.
ClientSdk getClientSdk(String clientSupplier) {
if (!sdks.containsKey(clientSupplier))
sdks.put(clientSupplier, beanFactory.getBean(ClientSdk.class, clientSupplier));
return sdks.get(clientSupplier);
}
// This is probably the most important bit. This creates a Spring Bean unique to a particular 'clientSupplier'
// value, but only does so when requested so that the factory can control when these beans are created, creating
// only one per a particular `clientSupplier` value.
#Bean
#Scope("prototype")
ClientSdk createSdk(String clientSupplier) {
return new MyClientSdk(clientSupplier);
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
#Service
class ClientRequestService {
#Autowired
SdkFactory factory;
public void sendRequestToClient(String clientSupplier) {
ClientSdk clientSdk = factory.getClientSdk(clientSupplier);
clientSdk.sendRequestToClient();
}
}
#SpringBootApplication
public class HarmonyTestApp implements CommandLineRunner {
#Autowired
ClientRequestService service;
public static void main(String[] args) {
try {
ApplicationContext applicationContext = new SpringApplication(new Class<?>[]{HarmonyTestApp.class}).run(args);
} catch (Throwable e) {
e.printStackTrace();
}
}
#Override
public void run(String... args) {
service.sendRequestToClient("client1");
service.sendRequestToClient("client2");
service.sendRequestToClient("client1");
service.sendRequestToClient("client1");
service.sendRequestToClient("client2");
}
}
Result:
### Created MyClientSdk for: client1
Sending request for client: client1
CS: client1 Environment Prop: TestApp
### Created MyClientSdk for: client2
Sending request for client: client2
CS: client2 Environment Prop: TestApp
Sending request for client: client1
CS: client1 Environment Prop: TestApp
Sending request for client: client1
CS: client1 Environment Prop: TestApp
Sending request for client: client2
CS: client2 Environment Prop: TestApp
Note that per the output, each of client1's and client2's ClientSdk objects are only created once, even though they're used multiple times. Also notice that since the call to ident() in sendRequestToClient prints the value of a property obtained by an autowired Environment instance, autowiring of each ClientSdk object has worked.
I do realize that I used a String instead of a ClientSupplier object as the identifying key for each ClientSdk object. I did that just to keep the example as simple as I could. I expect you can expand the example to replace the clientSupplier String with an instance of ClientSupplier and somehow use that object as the key/identifier to insure that just one ClientSdk instance is created per ClientSupplier. That detail isn't really germain to the basic idea here.
Also, please note that the question itself changed while I was working on my implementation. Given that there are now exactly two subclasses of ClientSdk, you could simply make those regular #Component Spring beans. Having a small static number of those makes this problem less interesting. The technique I demonstrate here allows for an unlimited number of bean instances of ClientSdk class without having to define a unique class for each of them. This requires that Spring create arbitrary instances of them based on runtime information. This was what the original form of the question seemed to be asking for.

Injecting configuration dependency

I am creating a cache client wrapper using spring framework. This is to provide cache layer to our application. Right now, we are using redis. I have found out that spring-data-redis library is very good for creating my wrapper.
My application will pass a configuration POJO to my wrapper and will then use the interface that I will provide.
spring-data-redis provides an easy way to access redis using two variables.
RedisConnectionFactory
RedisTemplate<String, Object>
Although, I will be providing a better interface to my application with my interface functions like:
public Object getValue( final String key ) throws ConfigInvalidException;
public void setValue( final String key, final Object value ) throws ConfigInvalidException;
public void setValueWithExpiry(final String key, final Object value, final int seconds, final TimeUnit timeUnit) throws ConfigInvalidException;
I still want to provide RedisConnectionFactory and RedisTemplate beans.
My question is how to initialize my wrapper application with this configuration POJO?
Currently my configuration looks like this:
import java.util.List;
public class ClusterConfigurationProperties {
List<String> nodes;
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}
And my AppConfig.java looks like this:
import com.ajio.Exception.ConfigInvalidException;
import com.ajio.configuration.ClusterConfigurationProperties;
import com.ajio.validator.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
#Configuration
public class AppConfig {
#Autowired
private ClusterConfigurationProperties clusterConfigurationProperties;
#Autowired
private Validator validator;
#Bean
ClusterConfigurationProperties clusterConfigurationProperties() {
return null;
}
#Bean
Validator validator() {
return new Validator();
}
#Bean
RedisConnectionFactory connectionFactory() throws ConfigInvalidException {
if (clusterConfigurationProperties == null)
throw new ConfigInvalidException("Please provide a cluster configuration POJO in context");
validator.validate(clusterConfigurationProperties);
return new JedisConnectionFactory(new RedisClusterConfiguration(clusterConfigurationProperties.getNodes()));
}
#Bean
RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) throws ConfigInvalidException {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory());
redisTemplate.setKeySerializer( new StringRedisSerializer() );
redisTemplate.setHashValueSerializer( new GenericToStringSerializer<>( Object.class ) );
redisTemplate.setValueSerializer( new GenericToStringSerializer<>( Object.class ) );
return redisTemplate;
}
}
Here I am expecting a ClusterConfigurationProperties POJO as a bean in application which will be using the interface of wrapper.
But to compile my wrapper, I have created a null bean itself. Then when application uses it, there will be two beans, one of application and one of wrapper.
How should I resolve this problem?
Actually what i wanted was to have cluster config as a bean in my client application. For that i dont need to declare #autowire clusterconfig in my wrapper application. Instead should take cluster config as a parameter in the method, so that the client will pass cluster config object when creating bean. And the bean which is created in client code should have code for creating redis connection factory.
But all this i was writing was to make my client unknown of redis. So, better solution is to have wrapper class which takes cluster config pojo and create redis connection factory etc. And client should create this wrapper as a bean.
Very poor concept of spring and design patterns lead me to this mistake.

How to use CDI into JAX-RS client

I have searched a while on SO and official documentation but I cannot found a way to use directly CDI injection into a JAX-RS client.
I retrieve a client using the builder method and I want to register a WriterInterceptor (or any filter like component) which uses injection to retrieve another bean.
I want to use CDI injection and avoid registering each bean with HK2.
ClientBuilder.newBuilder()
.register(MyWriter.class)
.build();
And MyWriter with the injected class.
#Provider
public class MyWriter implements WriterInterceptor {
private final MyRepo repo;
#Inject
public MyWriter(MyRepo repo) {
this.repo = repo;
}
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
context.proceed();
}
}
public class MyRepo {
}
I am running in an embedded jetty with Jersey 2 and Weld SE.
Its possible to inject in java se application using wield .
#Singleton
public class Application {
private static Logger logger = LoggerFactory.getLogger(Application.class);
#inject
private SomeOtherBean injectedBean;
public void run() {
logger.debug("application initialized");
injectedBean.doSomething();
}
}
inside main initialize weild
import java.io.IOException;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
public class EntryPoint {
public static void main(String[] args) throws IOException {
Weld weld = new Weld();
WeldContainer container = weld.initialize();
Application application = container.instance().select(Application.class).get();
application.run();
weld.shutdown();
}
}
Have a look at below doc
https://docs.jboss.org/weld/reference/latest/en-US/html/environments.html#_java_se
also below tutorial
https://randling.wordpress.com/2011/08/15/cdi-in-java-se/
If I understand everything correctly, this has already been asked and answered. In a nutshell: you have to override the default behaviour of the H2K Binder, so it reaches for the Weld Bean Manager. You don't have to register every Bean with H2K later on.
Edit: to contain everything in the post, so you don't have to read the comments:
The linked answer is for the server-side, not the client.
With standard tools (Jersey Client-side injection providers and the Weld bridge), it seems to be a too big overhead/impossible to do
Apparently in the Dropwizard project they managed to do custom client-side injection.

Using Spring autowired Service classes within a Liferay Indexer

I am using Spring #Service classes in my liferay portlet to get and store data. They are injected using the #autowired annotation. Everything is working as expected. When I am trying to use that same approach in a Liferay BaseIndexer subclass (to put data in the search engine) the #autowired annotated classes are all null (not injected).
Is there a way to get these Service classes in the Indexer ?
Best regards,
Daniel
This indexer is not instantiated by Spring, so you won't be able to autowired your service.
But, you could implement a custom ApplicationContextProvider (implementing Spring ApplicationContextAware) and use it in order to inject your service. It should be easy.
You should start creating this class, and let Spring to discover it (be sure that this class is scanned by spring):
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* Created by Alberto Martínez Ballesteros on 18/03/16.
*/
#Component("applicationContextProvider")
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context = null;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
Then, you can use this ApplicationContextProvider to inject your service within the indexer class:
For example:
public class CategoryIndexer extends BaseIndexer {
private CategoryService categoryService;
[....]
#Override
protected void doReindex(String className, long classPK) throws Exception {
if (categoryService == null) {
initService();
}
final Category category = categoryService.get(classPK);
doReindex(category);
}
private void initService() {
categoryService = (CategoryService) ApplicationContextProvider.getApplicationContext()
.getBean("categoryService");
}
[....]
As you can see, you can not use #Autowired in this way, but you can inject your bean anyway.
Regards.

Spring Pre/Post method security annotations not working

I can't seem to get Spring Pre/Post method security annotations to work. I've read every related stackoverflow question on the topic, and the main suggestion is to ensure that global-method-security is enabled in the same context as the beans which you wish to secure. I have the following my dispatcher-servlet.xml:
<context:component-scan base-package="com.package.path" />
<context:annotation-config />
<security:global-method-security pre-post-annotations="enabled" />
The beans in question are in "com.package.path". I know that Spring is creating instances of them correctly, as injection is working just fine and requests are being serviced by the intended classes.
So, here's an example service class in "com.package.path":
#Controller
#RequestMapping("/article")
public class ArticleServiceImpl extends GWTController implements ArticleService {
#Autowired
public ArticleServiceImpl(DataSource ds) {
}
#Override
#PreAuthorize("hasRole('ROLE_BASIC_USER')")
public Article save(Article article) {
}
}
The annotation on the save method does not work. A few important notes:
I'm using GWT, though from what I've read, that shouldn't matter much.
I have method security working perfectly well in another, similar project. The only difference is that there is a DAO layer in the other project, which is not present in this one. It's in this layer that I have annotation security working. However, it shouldn't matter what "layer" this is, as long as Spring is responsible for creation of the beans, right?
The interface "ArticleService" above is a GWT service interface. I've tried putting the annotation there, but that doesn't work either.
Here's my GWTController class, referenced above, if needed:
package com.areahomeschoolers.baconbits.server.spring;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.areahomeschoolers.baconbits.server.util.ServerContext;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
/**
* Spring controller class that handles all requests and passes them on to GWT. Also initializes server context.
*/
public class GWTController extends RemoteServiceServlet implements ServletConfigAware, ServletContextAware, Controller, RemoteService {
private static final long serialVersionUID = 1L;
protected ServletContext servletContext;
#Override
public ServletContext getServletContext() {
return servletContext;
}
// Call GWT's RemoteService doPost() method and return null.
#Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// load our ServerContext with current request, response, session, user, appContext, etc.
ServerContext.loadContext(request, response, servletContext);
try {
doPost(request, response);
} finally {
ServerContext.unloadContext();
}
return null; // response handled by GWT RPC over XmlHttpRequest
}
#Override
public void setServletConfig(ServletConfig conf) {
try {
super.init(conf);
} catch (ServletException e) {
e.printStackTrace();
}
}
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
#Override
protected void checkPermutationStrongName() throws SecurityException {
return;
}
#Override
protected void doUnexpectedFailure(Throwable e) {
e.printStackTrace();
super.doUnexpectedFailure(e);
}
}
Security aspect provided by Spring Security inherits all limitations of Spring Framework proxy-based AOP support. In particular, aspects are not applied to calls that happen "inside" the objects (unless you use AspectJ weaving), see 7.6.1 Understanding AOP proxies.
So, if you want to use security aspect this way, you need to use GWT integration mechanism that make calls to your service from the outside, i.e. a mechanism that doesn't require your services to extend RemoteServiceServlet.
Something like spring4gwt should work fine.

Resources