Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

An application scoped bean instantiated twice #8960

Closed
miron4dev opened this issue Apr 29, 2020 · 12 comments
Closed

An application scoped bean instantiated twice #8960

miron4dev opened this issue Apr 29, 2020 · 12 comments
Labels
kind/bug Something isn't working triage/wontfix This will not be worked on

Comments

@miron4dev
Copy link

miron4dev commented Apr 29, 2020

Describe the bug
I want to create a bean only once during the application lifetime.
I have the next configuration

@ApplicationScoped
public class RedisConfiguration {

@Produces
@ApplicationScoped
    public JedisPool JedisPool() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(poolSize);
        poolConfig.setMaxIdle(poolSize);
        poolConfig.setJmxEnabled(false); // GraalVM doesn't support JMX

        return new JedisPool(poolConfig, host, port, timeout, null, database, ssl);
    }
}

and inject JedisPool somewhere else.

Expected behavior
JedisPool constructor is called only once. So, I can guarantee that nobody instantiates this class except this call. Weld (at least weld-se) provides exactly the same behavior.

Actual behavior
JedisPool constructor is called twice. The first call with default no-args constructor, and the second one initiated by the method above.

Environment (please complete the following information):

  • Output of uname -a or ver: Darwin localhost 19.0.0 Darwin Kernel Version 19.0.0: Thu Oct 17 16:17:15 PDT 2019; root:xnu-6153.41.3~29/RELEASE_X86_64 x86_64
  • Output of java -version: java version "1.8.0_231"
  • Quarkus version or git rev: 1.4.1.Final
  • Build tool (ie. output of mvnw --version or gradlew --version): Apache Maven 3.6.2

Additional context
Why it is important: in this particular case I call poolConfig.setJmxEnabled(false); to make it work in the native mode because GraalVM is having some troubles with JMX. By default, JMX flag is enabled and the application is failing.
@Singleton annotation solves this issue, but in this case, I can't mock this bean using QuarkusMock

@miron4dev miron4dev added the kind/bug Something isn't working label Apr 29, 2020
@miron4dev miron4dev changed the title Creating an application scoped bean with a producer entails multiple instantiations An application scoped bean instantiated twice Apr 29, 2020
@geoand
Copy link
Contributor

geoand commented Apr 29, 2020

I think this is expected due to the creation of a proxy - @mkouba or @manovotn can confirm or deny.

You can use @Singleton if you want to not create a proxy at all.

@mkouba
Copy link
Contributor

mkouba commented Apr 29, 2020

@geoand is right. For a normal scoped bean a client proxy is always created when obtaining an injectable reference. This behavior is defined by the CDI spec.

AFAIK in weld-se (or at least in some versions) the relaxed construction is enabled by default. And it's implemented using sun.misc.Unsafe#allocateInstance() which allows you to create an instance without calling the constructor. However, Unsafe is non-portable and definitely not a good way to go. In Quarkus, we generate a no-args constructor if missing and needed (normal scoped bean).

Do you control the JedisPool class? I suppose you don't because you're using a producer method. In that case I'm afraid we can't do much about it (except for using non-normal scope).

@manovotn
Copy link
Contributor

I agree. This is standard behavior and Weld w/o relaxed construction should give you the same result.

Some of the above suggestions should do the trick.

@stuartwdouglas
Copy link
Member

You can probably just use @singleton instead of @ApplicationScoped.

@mkouba
Copy link
Contributor

mkouba commented Apr 30, 2020

You can probably just use @singleton instead of @ApplicationScoped.

@stuartwdouglas The problem is that @miron4dev would like to use QuarkusMock which only works with normal scopes and proxies ;-).

@gsmet
Copy link
Member

gsmet commented Apr 30, 2020

And there's absolutely no chance we could implement mocking for singletons?

@mkouba
Copy link
Contributor

mkouba commented Apr 30, 2020

And there's absolutely no chance we could implement mocking for singletons?

Not with the current test app lifecycle (one app for all tests).

@gsmet gsmet added the triage/wontfix This will not be worked on label Apr 30, 2020
@stuartwdouglas
Copy link
Member

You could do it via a bytecode transformer, basically just add that same client proxy mocking code into all @singleton beans.

@mkouba
Copy link
Contributor

mkouba commented May 4, 2020

You could do it via a bytecode transformer, basically just add that same client proxy mocking code into all @singleton beans.

Like modifying all the methods of a bean class? And if a mock exists then delegate to this mock instead of executing the method body? That could work. On the other hand, such a change looks quite "invasive".

@stuartwdouglas
Copy link
Member

stuartwdouglas commented May 4, 2020 via email

@geoand
Copy link
Contributor

geoand commented May 4, 2020

That should work. I am +1.

If we are worried it's to invasive, perhaps we would have a flag that the user needs to switch?

There is only a one case I can think of were mockito would still complain:
When the implementation of a bean is a dynamic proxy, we still won't be able to pass it to mockito. That shouldn't be too much of a problem.

@mkouba
Copy link
Contributor

mkouba commented May 4, 2020

It's only for tests

Yes, I know. My point is that the bigger the difference between the test and the prod environment is the bigger chance for unpredictable bugs. But that's a general problem with mocking ;-)

implementation of a bean is a dynamic proxy

Ehm, what is it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working triage/wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

6 participants