Igor Dumchykov
Igor Dumchykov

Reputation: 397

CDI context reload in runtime

I have CDI based application. At runtime my application generates custom event that causes bean reload. Bean reload means go through all beans and reinitialize already injected beans. Reinitialization should be aware of beans dependencies. For example:

class BeanA {

 String url;

 @PostConstruct
 void init(){
  url = UrlFactory.getNewUrl()
 }
}

class BeanB {

 @Inject
 BeanA beanA;

 @PostConstruct
 void init(){
  url = beanA.getUrl();
  doSomethingWith(url);
 }

Hence, when event comes, BeanA and BeanB should be reinitialized in strict order, so during BeanB reinitialization BeanB already aware of new url initialized in BeanA. Is it possible to do at runtime using already existing tools within CDI (something like in Spring: AutowireCapableBeanFactory)? I found that EJB already has @DependsOn annotation that is capable of building bean order during application start up.

The most brute force solution I came up with is listen to one of CDI events during application start up, collect all beans and build dependency graph and during reload go through this graph and do reinitialization. I'm afraid I'm not aware of many pitfalls that I might run into (like cyclic dependencies, lazy initialization (it might not work at all in this case) and many others I'm not aware of, because does not have pretty good understanding of how CDI container works internally)

Are there any other existing techniques that actually solve my problem?

Upvotes: 1

Views: 390

Answers (1)

Laird Nelson
Laird Nelson

Reputation: 16238

First, make a producer method that @Produces your String-typed url value (maybe create a @URL qualifier annotation to further identify it). Produce this in @Dependent scope. Something like:

@Produces @Dependent @URL private static final String produceUrl() {
  return UrlFactory.getNewUrl();
}

Now anywhere someone does @Inject @URL private String url they'll get a new String representation of a URL from your UrlFactory.

More importantly, anytime anyone does @Inject @URL private Provider<String> urlProvider; they'll get a Provider whose get() method, when invoked, will return the latest and greatest URL value.

This may be all you need if you look at it right.

If it's not all you need, then make BeanA do this:

@Inject
@URL
private Provider<String> urlProvider;

…and then in its (omitted above) getUrl() method, do this:

public String getUrl() {
  return this.urlProvider.get();
}

You'll get a new String each time.

If you've done that, then you can just add BeanA to your observer method and have it handed to you:

private static final void onEvent(@Observes final YourEvent event, final BeanA beanA) {
  final String url = beanA.getUrl(); // latest and greatest      
}

Upvotes: 1

Related Questions