How to inject singleton objects produced into factory - java

Even after going through similar questions on factory and dependency injection, I couldn't find an answer.
Let's say I have a EventHandler factory which gives out different kind of EventHandler based on event type. Each type of EventHandler has it's own dependencies used in handling that event and these could be different from other EventHandler type.
public interface EventHandler {
void handle(Event e);
}
public class EventHandlerA implements EventHandler{
private DependencyA depA;
//constructors for depA
}
public class EventHandlerB implements EventHandler{
private DependencyB depB;
//constructors for depB
}
public class Factory {
public EventHandler getHandler(EventType type) {
if(type.equals("A")) {
//return instance of EventHandlerA
}
}
}
#Configuration
public class Configuration {
#Bean
public EventHandler eventHandlerA() { return new EventHandler(depA()); }
#Bean
public Factory factory() { //.. ?? }
}
My question is - I am using Spring #Configuration with #Bean annotation to create singleton beans of all these event handlers and factory. How do I inject these beans of various event handlers into the factory so that factory can return these instances based on event type.
One direct approach I can think of is to just pass all the event handlers to factory during factory instantiation (via constructor) and maintain a mapping of type to handler and just do a get on that map. Downside of this approach is that whenever I need to add new event handler to the factory, I'll have to modify the constructor or add a new setter. Is there any better approach?

There are alot of ways to go about doing this but here are 2 ways.
In your question you have this:
public EventHandler eventHandlerA() { return new EventHandler(depA()); }
Now I dont know if you intended to have this but your bean will be a different instance at every injection because it creates a new one every return statement. You've essentially made your configuration into a factory. I am going to assume that this is unintended.
Method 1
I think this is the strategy I would use because you can add a boat load of handlers, you dont pollute your app context namespace and its clean and easy to follow. For this method I renamed EventHandlerFactory to EventHandlerRepository to make it more appropriate to its functionality.
When the configuration is loaded all the event handlers are added to the factory and then you can #Autowire that factory around your application then call repository.getHandler(/*Type*/).
This same design pattern could be used to make a real factory by simply renaming EventHandlerRepository to EventHandlerFactory then passing it Class<? extends EventHandler> instead of actual instances and creating a new instance on getHandler.
EventType:
public enum EventType {
A, B;
}
EventHandlerRepository:
public class EventHandlerRepository {
private Map<EventType, EventHandler> handlers;
public EventHandlerRepository() {
handlers = new HashMap<EventType, EventHandler>();
}
void synchronized registerHandler(EventType type, EventHandler handler) {
handlers.put(type, handler);
}
public synchronized EventHandler getHandler(EventType type) {
return handlers.get(type);
}
}
Configuration:
#Configuration
public class Configuration {
private EventHandlerRepository repository = new EventHandlerRepository();
public Config() {
// register all your event handlers here
factory.registerHandler(EventType.A, new EventHandlerA());
}
#Bean
public EventHandlerRepository getEventHandlerRepository() {
return repository;
}
}
Method 2
Simply add #Component to all your event handlers and make sure they are in your component scan path.
#Component
public class EventHandlerA implements EventHandler

Related

How to manually inject dependencies into a bean in a AfterBeanDiscovery event listener (CDI)

I am using CDI 1.2 (Weld 2.3.5) and want to manually instantiate a bean and inject its dependencies during an AfterBeanDiscovery event listener. I'm expecting that this would allow me to ensure that its Initialization would precede the #PostConstruct initialization of other beans in the deployment container.
However, I am looking to use a bean declared via #Produces as a dependency for the bean I am trying to create. According to the BeanManager API doc, I am not allowed to call a BeanManager.getReference() method during the AfterBeanDiscovery event. So I find myself a little stuck; I'm not sure if/how I can retrieve/use a dependency declared in the container as a parameter when constructing my bean in my ABD listener.
So far, I have the following:
// Producer bean configuration
#Produces
#CustomConfigType
public CustomConfig auditConfig() {
CustomConfig config = new CustomConfig();
config.setConfigFile("config/myconfig.properties");
config.setDataSource(datasource);
config.setResourceAccessor(new ClassLoaderResourceAccessor(getClass().getClassLoader()));
return config;
}
// CDI Extension
public class CDIBootstrap implements Extension {
void afterBeanDiscovery(#Observes AfterBeanDiscovery abd, BeanManager bm) {
AnnotatedType<CDICustomConfig> at = bm.createAnnotatedType(CDICustomConfig.class);
// this clearly fails as I am not allowed to use forEach() and retrieve an instance of the CustomConfig as declared using the #Produces annotation. But my goal is to accomplish something like this
CDI.current().select(CustomConfig.class, new AnnotationLiteral<CustomConfigType>(){}).forEach(config -> {
Bean instance = new Bean<CDICustomConfig>() {
#Override
public Set<Type> getTypes() {
Set<Type> types = new HashSet<>();
types.add(CDICustomConfig.class);
types.add(Object.class);
return types;
}
#Override
public Class<? extends Annotation> getScope() {
return Dependent.class;
}
...
// Bolierplate methods
...
#Override
public Set<InjectionPoint> getInjectionPoints() {
return it.getInjectionPoints();
}
#Override
public CDICustomConfig create(CreationalContext<CDICustomConfig> ctx) {
// I would like to use the #Produces bean reference here in the instance constructor
CDICustomConfig instance = new CDICustomConfig(config, config.getDataSource(), config.getResourceAccessor());
it.inject(instance, ctx);
it.postConstruct(instance);
return instance;
}
#Override
public void destroy(CDICustomConfig instance, CreationalContext<CDICustomConfig> ctx) {
it.preDestroy(instance);
it.dispose(instance);
ctx.release();
}
};
abd.addBean(instance);
});
}
I'm still learning how to best leverage the JEE/CDI events and listeners, so perhaps this is not the right approach. Is there anyway to legally do what I am hoping to do? Is there any way to use/access a CDI controlled bean instance from within the ABD event listener?
I'm looking to support multiple #Produces CustomConfig beans, and generate a CDICustomConfig for each instance of a CustomConfig bean found in the context. How should I approach this?

Spring conditional Injection at run time

I was looking at the spring annootation #Conditional to make a runtime conditional wiring of my dependency. I have a service that takes a value in the constructor. I want to create 2 instances of the service with different constructor inputs, then based on a condition at run time, I want to use this bean or that bean. Looks like #Conditional is evaluated on startup time. Is there another way to make my example work on runtime?
You want to create 2 (or more, for that matter) instances and then only use one of them at runtime (what means, it could possibly change over the life of your application).
You can create a holder bean that would delegate the calls to the correct bean.
Let's assume you have:
interface BeanInterface {
// some common interface
void f();
};
// original beans a & b
#Bean
public BeanInterface beanA() {
return new BeanAImpl();
}
#Bean
public BeanInterface beanB() {
return new BeanBImpl();
}
Then create a wrapper bean:
class Wrapper implements BeanInterface {
public Wrapper(BeanInterface... beans) { this.delegates = beans };
private BeanInterface current() { return ... /* depending on your runtime condition */ }
#Override
public void f() {
current().f();
}
}
And obviously you need to create the wrapper in your configuration
#Bean
public BeanInterface wrapper() {
return new Wrapper(beanA(), beanB());
}

Guice FactoryModuleBuilder an instance with constructor parameters

I´m using Guice to initalize a class with some arguments from a config file
#Provides
#Singleton
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
This class is injected in another class which is singleton as well.
class A{
#Inject private RetryServiceCaller retryServiceCaller;
}
But now the problem is that since this new class A is singleton, I need to clone the retryServiceCaller every time that somebody use this class A.
I´ve been investigating FactoryModuleBuilder to use it and create a factory for this class. But since the class has parameters from the config file I could not find the way to make it works.
Something like this
class A{
#Inject private RetryServiceCaller.Factory retryServiceCallerFactory;
}
Then in my RetryServiceCaller implement this
public interface Factory {
#Inject
RetryServiceCaller create();
}
#Inject
public RetryServiceCaller(int maxRetryAttempts, int upperBoundary, int lowerBoundary, long multiplicationFactor, int timeout, int incrementTimeout) {
this.maxRetryAttempts = maxRetryAttempts;
this.upperBoundary = upperBoundary;
this.lowerBoundary = lowerBoundary;
this.multiplicationFactor = multiplicationFactor;
this.timeout = timeout;
this.incrementTimeout = incrementTimeout;
}
But guice throw me errors saying
No implementation for com.proxy.handlers.RetryServiceCaller$Factory was bound
Guice can automatically provide a zero-argument factory: Instead of injecting Foo, you can always inject Provider<Foo>. This allows you to call fooProvider.get() to create an instance whenever and wherever you'd like. You don't have to bind to a Provider or use a Provides method to get access to this; you can inject Foo or Provider<Foo> whether you use a bind(...).to(...) type binding, a toProvider binding, a toInstance binding, a #Provides method, or anything else, and Guice will call get or return an internal Provider automatically.
(The returned Provider will also respect scopes, so you'll need to drop your #Singleton scope in order to get more than one instance, and be aware that toInstance bindings will always return the same instance.)
This is not a job for FactoryModuleBuilder; only use FactoryModuleBuilder when you need to mix injected and non-injected constructor parameters in the same type.
Your finished binding should look like this:
#Provides
/* NOT #Singleton */
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
And in your class:
#Inject public YourCallerConsumer(Provider<RetryServiceCaller> callerProvider) {
this.callerProvider = callerProvider;
}
public void doAction() {
RetryServiceCaller newCaller = callerProvider.get();
// interact with caller
}
Your first approach should work just fine. If you don't want the RetryServiceCaller to be a singleton, remove the #Singleton annotation from the provider method, and a new instance will be created for every injection point.
Assisted inject could work here too, but it's overkill. If you want to go that route:
interface RetryServiceCallerFactory {
RetryServiceCaller create(String configParam1, String configParam2);
}
public class RetryServiceCaller {
#AssistedInject
public RetryServiceCaller(String configParam1, String configParam2) {}
}
then, in your module
install(new FactoryModuleBuilder().build(Factory.class);
and in your injection points
#Inject RetryServiceCallerFactory factory;
RetryServiceCaller create(JsonObject config) {
return factory.create(config.getFirstParam(), config.getSecondParam());
}
You can refer to the documentation for more extensive examples.

How can I select Spring bean instance at runtime

Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}

Inject cached instances using cdi

I want expose instances managed by an external framework to CDI applications using #Inject. These instances must be provided this other framework since their lifecycle is based on various caching strategies.
Ex: same instance is visible within same thread scope, might live across many request scopes, session scope is not applicable. Seems I need to define a new scope targeting these kind of instances?
What is the best way to do this? An extension, is it possible with producer methods?
I almost got it to work with producer methods using the following:
#Inject
#CustomInject
FwObject obj;
#Produces
#CustomInject
FwObject createConfig(InjectionPoint p) {
return (FwObject) ctx.get((Class<?>) p.getType());
}
But this force me to be explicit about the type produced which is not possible since there is no common framework interface.
Any help appreciated.
Maybe with producer methods, all depends on what you need, but an extension is probably the best way to go. If you need to go with a new scope (if you're using JSF the Conversation scope may work) you will certainly need to create an extension.
I think I solved it by creating a custom scope. The following article was really helpful:
http://www.verborgh.be/articles/2010/01/06/porting-the-viewscoped-jsf-annotation-to-cdi/
This is a very brief description of how I solved it.
Create custom scope annotation.
import javax.enterprise.context.NormalScope;
#Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
#NormalScope
public #interface CustomScope {
}
Create custom context.
import javax.enterprise.context.spi.Context;
public class CustomContext implements Context {
private MyFw myFw = .... ;
#Override
public Class<? extends Annotation> getScope() {
return CustomScope.class;
}
#Override
public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
Bean bean = (Bean) contextual;
return (T) myFw.get(bean.getBeanClass());
}
#Override
public <T> T get(Contextual<T> contextual) {
Bean bean = (Bean) contextual;
return (T) myFw.get(bean.getBeanClass());
}
#Override
public boolean isActive() {
return true;
}
}
Create extension and register context.
import javax.enterprise.inject.spi.Extension;
public class CustomContextExtension implements Extension {
public void afterBeanDiscovery(#Observes AfterBeanDiscovery event, BeanManager manager) {
event.addContext(new CustomContext());
}
}
Register extension.
Add CustomContextExtension to META-INF/javax.enterprise.inject.spi.Extension
Add CustomScope to framework object.
#CustomScope
public class FwObject { ... }
Inject FwObject using #Inject where needed.
public class MyService {
#Inject
FwObject obj;
}

Categories

Resources