0
votes

I want do create a CDI factory using @Produces annotation on a method in order to look up a named something in an external service and return (produce) a proxy to this something in the external service to be injected into other beans. From a use case point of view, usually all these dependencies are required; however it happens sometimes that the requested something does not exist in the remote service and the application can react to this fact.

Basically the structure is like this:

@Singleton public class SomeBean {
  @Inject @MyName("required") private Something requiredSomething;
  @Inject @MyName("optional") private Instance<Something> optionalSomething;

  @PostConstruct void init() {
    if (optionalSomething.isUnSatisfied()) {
      // act without optional Something in external service
    } else {
      optionalSomething.get().doWhatIWant(NOW);
    }
  }
}

@ApplicationScoped
public class SomethingFactory {
  @Inject private RemoteSomethingService remoteService;

  @Produces
  @MyName(value = "")
  public Something createRemoteSomething(InjectionPoint injectionPoint) {
    MyName name = injectionPoint.getAnnotated().getAnnotation(MyName.class);
    Something some = remoteService.lookup(name.value()); // throws an exception if lookup fails
    return some;
  }
}

This works in cases the injection on the side that uses somethings is required: successfull lookups inject the looked up Something instance, while unsuccessful lookups break the bean bootstrapping. However, in cases of an optional injection with a non-existent something, it either breaks because Instance.isUnSatisfied() returns false. If I leave createRemoteSomething() throwing the exception, this exception is raised during the call to optionalSomething.get(); if I return null instead, the same call leads to a NPE.

How to achieve this using CDI primitives? Thanks.

1

1 Answers

0
votes

Your producer method is just fine (I'm assuming its return type is supposed to be Something). You are producing a Something in @Dependent scope (the default).

Things that are in @Dependent scope may be null. In general nothing else may be. (There are some leftover bits in CDI from ancient times when other scopes could produce null objects but they are the remnants of a bygone era.)

So in your case, simply return null from your producer method when you cannot produce what you want, but this is not an error condition (otherwise obviously throw an exception of some kind).

Then, on the consuming side, you're almost there.

Instance#isUnsatisfied() returns true only when there is no producer method (or other kind of bean) that can "make" the thing being asked for. In your case, there is a producer method that can "make" the thing being asked for, so isUnsatisfied() returns false, and that is proper.

All you should have to do is change this:

optionalSomething.get().doWhatIWant(NOW);

…to:

final Something something = optionalSomething.get();
if (something == null) {
  // no such Something, but an expected case
} else {
  something.doWhatIWant(NOW);
}

Again, this only works if Something is in @Dependent scope (and not, say, @ApplicationScoped scope) which in your case is indeed the case.