0

This post is related to an older SO Post of mine, wherein I was trying to understand the requirements of a no-args constructor by WELD.

Right now, I'm trying to figure out if there is a way in CDI to inject an @ApplicationScoped bean (@Normal) into a @Dependent scope. From what I've read from WELD, the requirements are to have a non-private no-arg constructor to be proxyable. However, I do not have control over the bean definition as it is provided by a library. My code is doing the following:

    @Produces
    @ApplicationScoped
    @Named("keycloakAdmin")
    public Keycloak getKeycloakAdminClient(@Named("keycloakDeployment") final KeycloakDeployment deployment) {
        String clientId = deployment.getResourceName();
        Map<String, Object> clientCredentials = deployment.getResourceCredentials();

        // need to set the resteasy client connection pool size > 0 to ensure thread safety (https://access.redhat.com/solutions/2192911)
        ResteasyClient client = new ResteasyClientBuilder().connectionPoolSize(CONNECTION_POOL_SIZE).maxPooledPerRoute(CONNECTION_POOL_SIZE)
                .defaultProxy("localhost",8888)
                .build();

        KeycloakBuilder builder = KeycloakBuilder.builder()
                .clientId(clientId)
                .clientSecret((String) clientCredentials.get(CredentialRepresentation.SECRET))
                .realm(deployment.getRealm())
                .serverUrl(deployment.getAuthServerBaseUrl())
                .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
                .resteasyClient(client);

        return builder.build();
    }



    // error thrown here that cannot inject @Normal scoped bean as it is not proxyable because it has no no-args constructor
    @Produces
    @Dependent
    @Named("keycloakRealm")
    public RealmRepresentation getKeycloakRealm( @Named("keycloakAdmin") final Keycloak adminClient ){
        // error thrown here that cannot inject @Normal scoped bean as it is not proxyable because it has no no-arg
        return adminClient.realm(resolveKeycloakDeployment().getRealm()).toRepresentation();
    }

The problem is that I do not control the Keycloak bean; it is provided by the library. Consequently, I have no way of providing a no-argument constructor to the bean.

Does this mean it is impossible to do? Are there any workarounds that one can use? This would seem like a significant limitation by WELD, particularly when it comes to @Produceing 3rd party beans.

My goal is to have a single Keycloak bean for the application as it is thread-safe and only needs to be initialized once. However, I want to be able to inject it into non-application-scoped beans.

There is a @Singleton scope which may address my issue, but if @Singleton works for this case, what is the purpose of the 2 different scopes? Under what circumstances would one want a non-proxied singleton (@Singleton) vs a proxied one (@ApplicationScoped)? Or is @Singleton for the entire container, whereas @ApplicationScoped for the application (WAR) only instead? How does it apply to an EAR or multiple ears?

Eric B.
  • 23,425
  • 50
  • 169
  • 316
  • I'm gonna keep this a as comment because I am not sure it will work, but do give it a shot. The requirement to have this constructor is a technical limitation of Java and it is used to create the proxy object. *However*, you might be able to bypass this by using Weld's feature called [Relaxed Construction](http://docs.jboss.org/weld/reference/latest-master/en-US/html_single/#relaxedConstruction). This will basically allocate proxies without calling constructor whatsoever. Note that since it's Weld feature, it makes your app non-portable (cannot run it on OWB for instance). – Siliarus May 31 '18 at 13:03
  • @Siliarus Thanks - I saw that option, but the non-portability of it has some concern to me, but maybe not too much. The bigger concern was the blurb I read on a redhat or wildfly or weld site indicating that all deployments in the container must have the flag set. So it isn't possible to enable the relaxed constructor for individual apps/contexts. – Eric B. May 31 '18 at 15:51
  • Where exactly did you read that? You cannot have that for specific context, that is true, but you can have it for one deployed app and not have it for another. It's about how you set it, if you set it directly in WildFly's Weld subsystem, then all apps have it, if you use `weld.properties` in one of your apps, only that app should have it. – Siliarus Jun 01 '18 at 05:48
  • Alternatively, you could also keep the bean `@Dependent` and store a reference to in in the producing bean. The producer method would check that reference, if it's null create it, store and return, otherwise just return that same instance every time. – Siliarus Jun 01 '18 at 08:22
  • Or yet another way - keep `Keycloak` bean `@Dependent` and create an application scoped bean (`Foo`), which will inject your `Keycloak` bean. Now you can work with your `Foo` bean which is truly app scoped and has `Keycloak` in it (and `@Dependent` scope will live as long as the scope into which you inject it, hence it will effectively be app scoped). – Siliarus Jun 01 '18 at 08:24
  • @Siliarus I found it in a comment burried in a Jira ticket https://issues.jboss.org/browse/WELD-2448?focusedCommentId=13513272&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13513272 – Eric B. Jun 01 '18 at 19:00
  • @Siliarus I had already considered the Singleton pattern option, but hadn't thought of the `@Dependent` in an `@ApplicationScoped` bean, however, what I don't like in either case is that my consuming code needs to be "wrapper-aware". That is, I can't inject a `Keycloak` bean, but rather need to inject a `KeycloakWrapper` bean which then has a `public Keycloak getBean()` method. I was hoping to avoid that, looking for a Factory style solution like Spring uses (in which you can declare a factory, but it knows to automatically return the `getObject()` bean when it is declared/injected) – Eric B. Jun 01 '18 at 19:06
  • The JIRA issue is unrelated to your problem (but related to relaxed construction). I understand what you aim to do, I am merely tossing workarounds here. You could also maybe avoid wrapper-aware code with [`@Typed`](http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#restricting_bean_types) on the wrapping bean.But it would be a tad bit more complex to implement but usage would then be straightforward as you want it. – Siliarus Jun 02 '18 at 22:02
  • @Siliarus I liked the idea of the `@Typed`, but unless you have a brilliant idea how I can workaround this issue, I think I'm stuck. The Keycloak class doesn't implement any interface and its only constructor is package protected. So short of creating my own class in the same package (and hope the jar isn't sealed), that extends the Keycloak class, I can't for the life of me, figure out how I could create any wrapper/proxy/etc without forcing the consuming code to know that it is a wrapper. My CDI knowledge is still limited, so if you have any suggestions, please let me know. – Eric B. Jun 04 '18 at 14:29
  • Oh, I thought you could extend the class...hmm, in that case you cannot really do what I suggested. Have you tried CDI's `@Singleton`? The main difference compared to app scoped is that singleton has no proxy. However, that comes with possible serialization issues, as described [here](http://docs.jboss.org/weld/reference/latest-master/en-US/html_single/#_the_singleton_pseudo_scope). I don't know details about your structure so it's hard to say whether that would actually pose a problem for you. Might also be worth a shot asking Keycloak devs about the intended way to leverage CDI with Keycloak – Siliarus Jun 05 '18 at 08:11
  • @Siliarus That's what I did; used `@Singleton`. But I have to trust anyone using my bean to ensure that they inject it as a `transient` field or using `Instance` to inject it. Otherwise, potential serialization errors could happen. Was just trying to protect against that. – Eric B. Jun 05 '18 at 17:21
  • Should work though. Anyway, I also checked with Keycloak guys and learnt that there is currently no official CDI support as there were no calls for it. Maybe you could create a JIRA ticket for them and see if there are more people who want it. I guess they would implement it then. – Siliarus Jun 06 '18 at 07:02
  • Could you please update your question with the exact stack trace and dependency declaration of Keycloak library you are using. – Illya Kysil Nov 30 '18 at 21:51

0 Answers0