In a JavaEE 6 environment (glassfish and WLS) we are using a CDI producer to inject configuration info into a stateless EJB like this
@Stateless
public class MyBusinessEJB {
@Inject @MyConfigurationQualifier
private String configValue;
public void someBusinessMethod() {
LOG.log( Level.INFO, "current config: {0}", configValue );
}
}
@Stateless
public class MyProducerEJB {
@Produces @MyConfigurationQualifier
public String getConfigurationFromDB() {
return ...;
}
}
Now it is possible that the configuration info changes from time to time in the database. But these changes are not reflected by the injected String in the EJB. I think thats ok because of the EJB lifecycle, where an EJB instance is long living to serve lots of clients. The configuration is injected at the very beginning of the EJBs life and is not renewed afterwards.
As a first workaround we tried to add @RequestScoped
to the producer method, which results in an Exception WELD-001435: ...String... is not proxyable...
. We understand that exception and tried to produce a Wrapper-Bean like
@Produces @MyConfigurationQualifier @RequestScoped
public Wrapper getConfigurationFromDB() {
return new Wrapper( originalStringValue );
}
public class Wrapper {
private String configValue;
public Wrapper() {} // needed for CDI proxying
public Wrapper( String configValue ) { this.configValue = configValue; }
public String get() { return configValue; }
}
This works fine - but it feels to be inconvenient using wrapper.get()
all the time.
As a second workaround we used Instance<String>
for injection. A Wrapper is not needed any more, even that @RequestScoped
can be ommitted.
@Stateless
public class MyBusinessEJB {
@Inject @MyConfigurationQualifier
private Instance<String> configValue;
public void someBusinessMethod() {
LOG.log( Level.INFO, "current config: {0}", configValue.get() );
}
}
@Stateless
public class MyProducerEJB {
@Produces @MyConfigurationQualifier
public String getConfigurationFromDB() {
return ...;
}
}
This works as well. But again we have to call instance.get()
in the business EJB. Of course Instance
also is some kind of wrapper.
Now the questions are:
Is it specified that Instance.get()
will always return renewed Objects even without annotating the producer method with @RequestScoped
? I cant find a hint in the javadoc or in the spec.
Is there a more convenient way to directy inject String
into the EJB without usage of a wrapper-class but with renewed data for each call of the EJB business methods (or maybe for each transaction or some defined time)?
Update:
Thanks to the comment by John I found this bug report and this FAQ. If I understand it correctly our second workaround will cause a memory leak because each call of Instance.get()
will put a new String instance to the EJBs scope which is quite the same as @ApplicationScope
. I can confirm this looking at the heapdump of our application. So we will continue with our first approach which seems to be ok.
Instance.get()
. Thanks for your hint! – frifle