billdoor
billdoor

Reputation: 2043

Can a CDI Bean's method return a managed Bean it created?

i have the following setup:

@Applicationscoped
@Transactional(txtype.Requires_new)
Public class querybean {

    @Inject ExternalSysrltem externalSystemProxy;

    Public Handle gethandleByKey(String key) {
        return new Handle(/*do external Systems Query, returns an ExternalHandle Object*/)
    }

    Public static class Handle {
        ExternalHandle eh;
        /*protected so that User of class cannot Instantiate it otherwise that by getHandleByKey()*/
        Protected Handle(ExternalHandle arg) {
            This.eh = arg;
        }

        Public String getHandleInfo() {
            Return This.eh.getName() + "/" + this.eh.getState()..;
            /*generally wrap the ExternallHandle with businesslogic to hide direct access to the complex ExternalService's Interface*/
        }
    }
}

Can I get Handle to be a Managed Bean that can be annotated with @Transactional and still create it in the getHandleByKey Method at Runtime by querying the external System?

Upvotes: 0

Views: 189

Answers (1)

Antoine Sabot-Durand
Antoine Sabot-Durand

Reputation: 4970

A static inner class can be a bean according the the spec. In your example it is not a bean due to its constructor. As said in comments you could use a producer, but a produced bean can't be intercepted (with @Transaction here)

If you want to keep your pattern, you'll have to create a very complex extension since it should work at low level to ensure interceptor will be activated.

I suggest that you go for something simpler by deporting your ExternalHandle resolution in Handle Bean, allowing you to use a String to construct it.

First create a qualifier with a non binding member to transmit information to your constructor.

@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
@Documented
@Qualifier
public @interface Keyed {

    @Nonbinding
    String key();
}

Then create a literal for your annotation to allow creation of an annotation instance with a given key value.

public class KeyedLiteral extends AnnotationLiteral<Keyed> implements Keyed {

    private final String key;

    public KeyedLiteral(String key) {
        this.key = key;
    }

    @Override
    public String key() {
        return key;
    }
}

Using programmatic lookup and InjectionPoint to transmit your key value. Your code will be like:

@Applicationscoped
@Transactional(txtype.Requires_new)
Public class querybean {
    @Inject
    @Any
    Instance<Handle> handles;

    Public Handle gethandleByKey(String key) {
        return instances.select(new KeyedLiteral(key)).get()
    }

    @Dependent
    @Transactional
    @Keyed("") //enforce the presence of the annotation for the constructor
    Public static class Handle {

        ExternalHandle eh;

        // needed to make the bean proxyable (mandatory for the interceptor bound)) 
        Protected Handle() {}

        @Inject
        Protected Handle(InjectionPoint ip, ExternalSysrltem externalSystem) {
            String key=ip.getAnnotated().getAnnotation(Keyed.class).key();
            eh = /*do external Systems Query, returns an ExternalHandle Object from key and externalSystem*/
        }

        Public String getHandleInfo() {
            Return This.eh.getName() + "/" + this.eh.getState()..;
            /*generally wrap the ExternallHandle with businesslogic to hide direct access to the complex ExternalService's Interface*/
        }
    }
}

Upvotes: 1

Related Questions