1
votes

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.

1
Are you definitely in a Java EE 6 environment? The reason being that the use of all of the depedent scoped beans will cause memory leaks.John Ament
@John Ament: Yes, we are. And up to now I do not know about memory leaks. Can you point me to some information on that?frifle
To the last question: you can populate the property manually in an @AroundInvoke-methodJaqen H'ghar
@John Ament: Now I can confirm the memory leak. I first had to learn how to handle all those proxy instances in the memory analyzer. And I found enough info about the problems concerning Instance.get(). Thanks for your hint!frifle

1 Answers

1
votes

Sorry, I won't answer your question, because IMHO what you want to achieve doesn't make much sense.

From my perspective you should write something like:

@ApplicationScoped
public class AppConfig {

    public String getConfigValue(String key) {
        // ...
        return value;
    }

    // launched by some scheduler from time to time
    public synchronized void refreshConfigurationFromDB() {
        // ...
    }
}

And then injecting only AppConfig instance:

@Stateless
public class MyBusinessEJB {

    @Inject
    private AppConfig config;

    // ...
}