CDI Conditionally produce/override a bean if a JNDI resource is present - java

I'm trying to find a way to conditionally produce an #Alternative/#Specialized bean in CDI 1.2 if a given JNDI resource is present.
My use case is that I have a default bean which uses an in-memory data structure. However, if a given JNDI resource is present, I want to use that resource instead in a different implementation of my service. My problem is that as soon as I use a #Resource(lookup='jndiName') annotation, Weld throws an exception if the resource is not found.
I wanted to use a producer to conditionally create the resource-based bean, but if I try to inject the #Resource and it is missing, Weld fails.
For example:
interface MyService{
void doSomething();
}
// In memory implementation that I always want to have available for injection if no other MyService bean implementation is available
#ApplicationScoped
public class InMemory implements MyService{
Map<String, String> persistence = new HashMap<>();
public void doSomething(){ persistence.put("now", new Date());}
}
// Bean to be available IFF a JNDI based cache is found
public class JndiPersistence implements MyService{
Cache<String, String> persistence;
public JndiPersistence(Cache persistence){ this.persistence = persistence);}
public void doSomething(){ persistence.put("now", new Date());}
}
// client class which uses the service
public class DataManager(){
private MyService myservice;
#Inject
public DataManager( MyService myservice ){ this.myservice = myservice; }
// calls the injected service bean
public void manageMyData(){
myservice.doSomething();
}
Finally, the producer:
public class JndiProducer{
#Resource(lookup="java:comp/env/persistenceCache")
Cache cache;
#Produces
#ApplicationScoped
public MyService jndiBean(){
return new JndiPersistence( cache );
}
}
I have tried changing my Resource injection to Instance<Cache> but if the jndi name is missing, it still throws an exception and doesn't even make it to the Producer. Finally, I am not sure how to make the entire Producer conditional on the resource being present and override the initial bean.
What is the correct approach for this in CDI? Is this even feasible, or is there a better approach to use instead?

The way I would go is a conditional producer with manual JNDI interactions.
I would start by making sure CDI ignores both implementations of MyService:
#Vetoed
public class InMemory implements MyService { ... }
#Vetoed
public class JndiPersistence implements MyService { ... }
Then go ahead with the producer (please note code is approximate, just to show the general principle, it might require adjustments/fine tuning):
// no real need for scoping the producer, unless the environment/configuration demands it
public class MyServiceProducer {
private MyService instance;
#Produces
#ApplicationScoped // this scoping applies to the produced bean
public MyService getMyService() {
// I *think* CDI guarantees that this will not be accessed concurrently,
// so no synchronization needed
if (instance == null) {
try {
InitialContext ic = new InitialContext();
Cache cache = (Cache) ic.lookup("java:comp/env/persistenceCache");
instance = new JndiPersistence(cache);
} catch (NamingException ne) {
// you can probably ignore it...
// use correct judgement though: you may want to log it
// or even fail altogether (i.e. rethrow it) e.g. if you sense
// that JNDI should be there but it is malfunctioning
instance = new InMemory();
}
}
return instance;
}
}

Related

How do I create multiple Spring beans of the same type without defining each one

I've seen a lot of workaround-looking things regarding what I'm trying to do using BeanDefinitionRegistryPostProcessor, but I wondered if there was a way to tap directly into Spring's bean creation API to override some behavior.
What I would like to see is something like this (note the 's' in #Components):
#Components(prefix="myBean-", numberOfInstances="${myapp.mybean.numberOfInstances}")
public class MyBean {
private final MyService myService;
public MyBean(final MyService myService) {
this.myService = myService;
}
#Scheduled(fixedDelayString = "${myapp.mybean.fixedDelay}")
public myJob() {
System.out.println("I'm working!");
}
}
I am basically looking for the same functionality of #Component where I can specify how many instances to make and just have the name generated.
As I mentioned before, the only way I have found to do this sort of thing (specifically for scheduled tasks now) is to use the BeanDefinitionRegistryPostProcessor to create the instances or create a custom SchedulingConfigurer to configure the tasks manually without using Spring beans, which means all the Runnable's dependencies have to be wired into the SchedulingConfigurer, and that just feels dirty.
Is this even possible--to add a new annotation to scan for and invoke some other way to create the beans?
Update
Thanks to #vince for helping me realize I don't need a separete bean for each job; I just have to configure the singleton multiple times into the FixedDelayTask.
#Component
public class MyBean {
private final MyService myService;
public MyBean(final MyService myService) {
this.myService = myService;
}
// Remove #Scheduled here since we have to configure multiple
// instances manually. This is where it would be nice to specify
// how many jobs of the same type you want.
// #Scheduled(fixedDelayString = "${myapp.mybean.fixedDelay}")
public myJob() {
System.out.println("I'm working!");
}
}
#Configuration
#EnableScheduling
public class MyBeanTaskConfiguration implements SchedulingConfigurer {
private final MyBean myBean;
public MyBeanTaskConfiguration(MyBean myBean) {
this.myBean = myBean;
}
#Override
public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
for (int i = 0; i < numberOfWorkers; i++) {
taskRegistrar.scheduleFixedDelayTask(
new FixedDelayTask(
myBean,
repeatIntervalMs,
repeatIntervalMs / numberOfWorkers * i + startDelayMs
)
);
}
}
}
Actually I'm wondering why u wanna do this. According to the IOC philosophy, beans should be delegated to container and clients don't need to care about beans' lifecycles. That's why Spring provides #Scope to support different bean scopes like singleton/request/session. So I don't think it a good way to control the specific number of a certain bean, besides, beans should theoretically be non-stateful, thus a single instance is fairly enough.
Prototype scoped beans will be provided as a new instance for each request to the container.
#Component
#Scope("prototype")
public class MyBean {
private final MyService myService;
public MyBean(final MyService myService) {
this.myService = myService;
}
// ...
}
// Get two separate instances
MyBean bean1 = (MyBean)applicationContext.getBean("myBean");
MyBean bean2 = (MyBean)applicationContext.getBean("myBean");

Loading Beans based on hostname

I am writing services in Spring boot that get their configurations from Spring cloud. These services are multi-tenant and the tenant is based on the host name.
what I have now is
public class MyController {
#Autowired
public MyController(MyServiceFactory factory) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#RequestHeader header, #PathVariable id) {
return factory.getMyService(header).handle(id);
}
}
where MyServiceFactory looks something like...
public class MyServiceFactory {
private final HashMap<String, MyService> serviceRegistry = new HashMap<>();
public MyService getMyService(String key) {
return serviceRegistry.get(key);
}
MyServiceFactory withService(String key, MyService service) {
this.serviceRegistry.put(key, service);
return this;
}
}
then in a configuration file
#Configuration
public ServiceFactoryConfiguration {
#Bean
public MyServiceFactory getMyServiceFactory() {
return new MyServiceFactory()
.withService("client1", new MyService1())
.withService("client2", new MyService2());
}
}
While what I have now works, I don't like that I need to create a factory for every dependency my controller may have. I'd like to have my code look something like this...
public class MyController {
#Autowired
public MyController(MyService service) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#PathVariable id) {
return service.handle(id);
}
}
with a configuration file like
#Configuration
public class MyServiceConfiguration() {
#Bean
#Qualifier("Client1")
public MyService getMyService1() {
return new MyService1();
}
#Bean
#Qualifier("Client2")
public MyService getMyService2() {
return new MyService2();
}
}
I can get the code that I want to write if I use a profile at application start up. But I want to have lots of different DNS records pointing to the same (pool of) instance(s) and have an instance be able to handle requests for different clients. I want to be able to swap out profiles on a per request basis.
Is this possible to do?
Spring profiles would not help here, you would need one application context per client, and that seems not what you want.
Instead you could use scoped beans.
Create your client dependent beans with scope 'client' :
#Bean
#Scope(value="client",proxyMode = ScopedProxyMode.INTERFACES)
#Primary
MyService myService(){
//does not really matter, which instance you create here
//the scope will create the real instance
//may be you can even return null, did not try that.
return new MyServiceDummy();
}
There will be at least 3 beans of type MyService : the scoped one, and one for each client. The annotation #Primary tells spring to always use the scoped bean for injection.
Create a scope :
public class ClientScope implements Scope {
#Autowired
BeanFactory beanFactory;
Object get(String name, ObjectFactory<?> objectFactory){
//we do not use the objectFactory here, instead the beanFactory
//you somehow have to know which client is the current
//from the config, current request, session, or ThreadLocal..
String client=findCurrentClient(..);
//client now is something like 'Client1'
//check if your cache (HashMap) contains an instance with
//BeanName = name for the client, if true, return that
..
//if not, create a new instance of the bean with the given name
//for the current client. Easiest way using a naming convention
String clientBeanName=client+'.'+name;
Object clientBean=BeanFactory.getBean(clientBeanName);
//put in cache ...
return clientBean;
};
}
And your client specific beans are configured like this :
#Bean('Client1.myService')
public MyService getMyService1() {
return new MyService1();
}
#Bean('Client2.myService')
public MyService getMyService2() {
return new MyService2();
}
Did not test it but used it in my projects. Should work.
tutorial spring custom scope

How to instantiate Spring managed beans at runtime?

I stuck with a simple refactoring from plain Java to Spring. Application has a "Container" object which instantiates its parts at runtime. Let me explain with the code:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
public void load() {
// repeated several times depending on external data/environment
RuntimeBean beanRuntime = createRuntimeBean();
runtimeBeans.add(beanRuntime);
}
public RuntimeBean createRuntimeBean() {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
}
}
Basically, during load container asks some external system to provide him information about number and configuration of each RuntimeBean and then it create beans according to given spec.
The problem is: usually when we do in Spring
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
Container container = (Container) context.getBean("container");
our object is fully configured and have all dependencies injected. But in my case I have to instantiate some objects which also needs dependency injection after I execute load() method.
How can I achieve that?
I am using a Java-based config. I already tried making a factory for RuntimeBeans:
public class BeanRuntimeFactory {
#Bean
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
Expecting #Bean to work in so called 'lite' mode. http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html Unfortunately, I found no difference with simply doing new RuntimeBean();
Here is a post with a similar issue: How to get beans created by FactoryBean spring managed?
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Configurable.html but it looks like a hammer in my case.
I also tried ApplicationContext.getBean("runtimeBean", args) where runtimeBean has a "Prototype" scope, but getBean is an awful solution.
Update 1
To be more concrete I am trying to refactor this class:
https://github.com/apache/lucene-solr/blob/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java
#see #load() method and find "return create(cd, false);"
Update 2
I found quite interesting thing called "lookup method injection" in spring documentation:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-lookup-method-injection
And also an interesting jira ticket https://jira.spring.io/browse/SPR-5192 where Phil Webb says https://jira.spring.io/browse/SPR-5192?focusedCommentId=86051&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-86051 that javax.inject.Provider should be used here (it reminds me Guice).
Update 3
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html
Update 4
The issue with all these 'lookup' methods is they don't support passing any arguments.. I also need to pass arguments as I would do with applicationContext.getBean("runtimeBean", arg1, arg2). Looks like it was fixed at some point with https://jira.spring.io/browse/SPR-7431
Update 5
Google Guice have a neat feature for it called AssistedInject. https://github.com/google/guice/wiki/AssistedInject
Looks like I found a solution. As I am using java based configuration it is even simpler than you can imagine. Alternative way in xml would be lookup-method, however only from spring version 4.1.X as it supports passing arguments to the method.
Here is a complete working example:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
private RuntimeBeanFactory runtimeBeanFactory;
public void load() {
// repeated several times depending on external data/environment
runtimeBeans.add(createRuntimeBean("Some external info1"));
runtimeBeans.add(createRuntimeBean("Some external info2"));
}
public RuntimeBean createRuntimeBean(String info) {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
return runtimeBeanFactory.createRuntimeBean(info);
}
public void setRuntimeBeanFactory(RuntimeBeanFactory runtimeBeanFactory) {
this.runtimeBeanFactory = runtimeBeanFactory;
}
}
public interface RuntimeBeanFactory {
RuntimeBean createRuntimeBean(String info);
}
//and finally
#Configuration
public class ApplicationConfiguration {
#Bean
Container container() {
Container container = new Container(beanToInject());
container.setBeanRuntimeFactory(runtimeBeanFactory());
return container;
}
// LOOK HOW IT IS SIMPLE IN THE JAVA CONFIGURATION
#Bean
public BeanRuntimeFactory runtimeBeanFactory() {
return new BeanRuntimeFactory() {
public RuntimeBean createRuntimeBean(String beanName) {
return runtimeBean(beanName);
}
};
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
RuntimeBean runtimeBean(String beanName) {
return new RuntimeBean(beanName);
}
}
class RuntimeBean {
#Autowired
Container container;
}
That's it.
Thanks everyone.
i think that your concept is wrong by using
RuntimeBean beanRuntime = createRuntimeBean();
you are bypassing Spring container and resorting to using regular java constructor therefore any annotations on factory method are ignored and this bean is never managed by Spring
here is the solution to create multiple prototype beans in one method, not pretty looking but should work, I autowired container in RuntimeBean as proof of autowiring shown in log also you can see in log that every bean is new instance of prototype when you run this .
'
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Container container = (Container) context.getBean("container");
container.load();
}
}
#Component
class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
#Autowired
ApplicationContext context;
#Autowired
private ObjectFactory<RuntimeBean> myBeanFactory;
public void load() {
// repeated several times depending on external data/environment
for (int i = 0; i < 10; i++) {
// **************************************
// COMENTED OUT THE WRONG STUFFF
// RuntimeBean beanRuntime = context.getBean(RuntimeBean.class);
// createRuntimeBean();
//
// **************************************
RuntimeBean beanRuntime = myBeanFactory.getObject();
runtimeBeans.add(beanRuntime);
System.out.println(beanRuntime + " " + beanRuntime.container);
}
}
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
// #Component
class RuntimeBean {
#Autowired
Container container;
} '
A simple approach:
#Component
public class RuntimeBeanBuilder {
#Autowired
private ApplicationContext applicationContext;
public MyObject load(String beanName, MyObject myObject) {
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext) applicationContext;
SingletonBeanRegistry beanRegistry = configContext.getBeanFactory();
if (beanRegistry.containsSingleton(beanName)) {
return beanRegistry.getSingleton(beanName);
} else {
beanRegistry.registerSingleton(beanName, myObject);
return beanRegistry.getSingleton(beanName);
}
}
}
#Service
public MyService{
//inject your builder and create or load beans
#Autowired
private RuntimeBeanBuilder builder;
//do something
}
Instead of using SingletonBeanRegistry you can use this:
BeanFactory beanFactory = configContext.getBeanFactory();
Anyway SingletonBeanBuilder extends HierarchicalBeanFactory and HierarchicalBeanFactory extends BeanFactory
You don't need the Container because all of the runtime objects should be created, held and managed by ApplicationContext. Think about a web application, they are much the same. Each request contains external data/environment info as you mentioned above. What you need is a prototype/request scoped bean like ExternalData or EnvironmentInfo which can read and hold runtime data through a static way, let's say a static factory method.
<bean id="externalData" class="ExternalData"
factory-method="read" scope="prototype"></bean>
<bean id="environmentInfo" class="EnvironmentInfo"
factory-method="read" scope="prototype/singleton"></bean>
<bean class="RuntimeBean" scope="prototype">
<property name="externalData" ref="externalData">
<property name="environmentInfo" ref="environmentInfo">
</bean>
If you do need a container to save the runtime objects, code should be
class Container {
List list;
ApplicationContext context;//injected by spring if Container is not a prototype bean
public void load() {// no loop inside, each time call load() will load a runtime object
RuntimeBean bean = context.getBean(RuntimeBean.class); // see official doc
list.add(bean);// do whatever
}
}
Official doc Singleton beans with prototype-bean dependencies.
It is possible to register beans dynamically by using BeanFactoryPostProcesor. Here you can do that while the application is booting (spring's application context has been initialized). You can not register beans latest, but on the other hand, you can make use of dependency injection for your beans, as they become "true" Spring beans.
public class DynamicBeansRegistar implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (! (beanFactory instanceof BeanDefinitionRegistry)) {
throw new RuntimeException("BeanFactory is not instance of BeanDefinitionRegistry");
}
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// here you can fire your logic to get definition for your beans at runtime and
// then register all beans you need (possibly inside a loop)
BeanDefinition dynamicBean = BeanDefinitionBuilder.
.rootBeanDefinition(TheClassOfYourDynamicBean.class) // here you define the class
.setScope(BeanDefinition.SCOPE_SINGLETON)
.addDependsOn("someOtherBean") // make sure all other needed beans are initialized
// you can set factory method, constructor args using other methods of this builder
.getBeanDefinition();
registry.registerBeanDefinition("your.bean.name", dynamicBean);
}
#Component
class SomeOtherClass {
// NOTE: it is possible to autowire the bean
#Autowired
private TheClassOfYourDynamicBean myDynamicBean;
}
As presented above, you can still utilize Spring's Dependency Injection, because the post processor works on the actual Application Context.

#inject doesn't work when the bean is not created via hk2

I have been trying to inject a service to a contract:
#org.jvnet.hk2.annotations.Contract
public interface CategoryDAO{
}
#org.jvnet.hk2.annotations.Service
public class HibernateCategoryDAO implements CategoryDAO
}
#org.jvnet.hk2.annotations.Service
public class MyCategoryManager implements CategoryManager {
#javax.inject.Inject
CategoryDAO categoryDAO;
}
But categoryDAO is null.
I tried even to bind it:
public class ServiceBinder implements Binder {
#Override
public void bind(DynamicConfiguration config) {
config.bind(BuilderHelper.link(HibernateCategoryDAO.class).to(CategoryDAO.class).build());
}
}
But still it stays null.
I init the MyCategoryManager with Reflections framework like this:
Set<?> managers = Reflections.getSubTypesOf(CategoryManager.class);
If you create your own object but still want it injected with hk2 you can use the ServiceLocator.inject method. In your case the descriptor HibernateCategoryDAO must be already in the ServiceLocator (perhaps using ServiceLocatorUtilities.addClass). Be aware that the instances you created yourself will not be managed or tracked by HK2 so they cannot be injected into other objects. Of course, you can add the objects you created yourself using ServiceLocatorUtilities.addOneConstant if you DO want it to be injected into other objects.
Hope that helps.

Need of ApplicationContext object reference in a bean?

What could be the possible usage of having a reference of applicationContext within a bean ?
It used to be useful to have a handle to an ApplicationContext or ConfigurableApplicationContext when writing infrastructure code. For example, if you wanted to write a service locator that should find all beans implementing an interface, you could
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void addFooServices() {
for (... : this.applicationContext.getBeansOfType(FooService.class)) {
// do something with each
}
These kinds of uses are diminishing, since you can now (3.2, if not earlier) just inject
#Autowired
private List<FooService> fooServices;
But it can still be useful to have your hands on the container at times, e.g. to trigger a container refresh from JMX, or some other hackery.
Will tell you one of our usage experience. We used the application context to get the collection of beans implementing a particular interface:
public class MyBean implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
Collection<IMyInterface> implementors = context.getBeansOfType(IMyInterface.class).values();
}
}

Categories

Resources