1

I have an EAR application with 2 modules. A WEB and an EJB module.

There are several services in the EJB module which are injected into classes of the WEB module, which works fine. But some services need configurations based on the user context which is only available in the WEB module. So I tried to add a producer to the WEB module.
The structure looks like this:

app-ear
|
+- app-ejb
|   |
|   +- Service
|   +- ServiceConnector
|
+- app-web
    |
    +- ServiceConnectorProducer

Here is a simplified version of that code:

Service class which gets a connector injected into: (EJB)

public class Service {
    @Inject
    private ServiceConnector connector;
}

Connector class which will handle the connection for a service: (EJB)

public class ServiceConnector {
    private final Config config;

    public ServiceConnector(final Config config) {
        this.config = config;
    }
}

Producer for ServiceConnector: (WEB)

public class ServiceConnectorProducer {
    @Produces
    public ServiceConnector produce(UserContext userCtx) {
        // ... create config and set data from user context
        return new ServiceConnector(config);
    }
}

At this point the producer is not recognized at the injection point and I get an unsatisfied dependency error:

org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type ServiceConnector with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject private Service.connector
  at Service.connector(Service.java:0)

This happen even though the producer was loaded by the container:

WELD-000106: Bean: Producer Method [ServiceConenctor] with qualifiers [@Any @Default] declared as [[BackedAnnotatedMethod] @Produces public ServiceConenctorProducer.produce(UserContext)]

When I make the ServiceConnector discoverable by the container by adding a default constructor I however get an ambiguous dependencies error:

Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001409: Ambiguous dependencies for type ServiceConnector with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject private Service.connector
at Service.connector(Service.java:0)
Possible dependencies: 
- Producer Method [ServiceConnector] with qualifiers [@Any @Default] declared as [[BackedAnnotatedMethod] @Produces public ServiceConnector.produce(UserContext)],
- Managed Bean [class ServiceConnector] with qualifiers [@Any @Default]

After that I tried the following things to resolve the ambiguous dependencies.
- https://stackoverflow.com/a/22985035 (Add qualifier)
- https://stackoverflow.com/a/36056972 (Use @Any)
- https://stackoverflow.com/a/29449040/11117616 (Use @Vetoed)

But all these solutions lead back to the unsatisfied dependency error.

WELD-001408: Unsatisfied dependencies for type ServiceConnector with qualifiers @SessionService
  at injection point [BackedAnnotatedField] @Inject @SessionService private Service.connector
  at Service.connector(Service.java:0)
WELD-001475: The following beans match by type, but none have matching qualifiers:
  - Managed Bean [class ServiceConnector] with qualifiers [@Any @Default]

So now I am out of ideas how to resolve the situation. How can I get the ServiceConnector to be produced in the WEB module?

Philipp S.
  • 13
  • 1
  • 4
  • Have you tried making the method `public Config produce(UserContext userCtx)` static? – ernest_k Feb 26 '19 at 08:08
  • Thanks for your reply. I totally forget to test that one. But unfortunately this doesn't resolve the issue. – Philipp S. Feb 26 '19 at 08:17
  • I'm not too sure your deploy/packaging structure is OK. It's unlikely that your web module's classpath includes your ejb jar. Maybe reading the spec on what is scanned from a web module will help. You may want to extract those producer/implementation classes into ear/lib. Otherwise, maybe having a classpath manifest entry including the ejb jar may help (although it's likely to be a very bad idea). – ernest_k Feb 26 '19 at 08:20
  • I'd bet injection into another class in your ejb-jar works... – ernest_k Feb 26 '19 at 08:21
  • The web module's classpath contains the ejb.jar, but not the other way around. Injection in general works for ejb->web and ejb->ejb. If I move the producer to ear/lib I lose the ability to access the user context from the web module. – Philipp S. Feb 27 '19 at 05:40
  • Maybe a relevant question for now is 'do you need the ejb module'? And If you do need ejbs, do you have a problem packaging them in the war file? (jee7 supports that) - you could reduce the entire thing to a single, war module. – ernest_k Feb 27 '19 at 05:46
  • The project is quite big and I am not the only developer. Changing the module structure at this point is not possible unfortunately. – Philipp S. Feb 27 '19 at 05:56

1 Answers1

3

You are most likely observing a Java EE umbrella spec visibility limitation. There are limitations to what certain archives (EAR/lib, EJB jar, WAR) in EAR can see and access. CDI then follows the same pattern with injection.

The implementation of these rules can very slightly differ based on how application servers interpret the specification. Now if you don't feel like reading the spec (who does, uh? ;-)), then you can look at this SO answer that kind of sums up it although not exhaustively.

Namely in your case the WAR file can access contents of EJB but not vice versa. In CDI terms that means that the EJB jar does not "see" the producer method you have there.

As for the ambiguous dependency exception you saw when you made ServiceConnector a bean - I am not 100% sure what that is without a reproducer and some debugging. It could be a bug in how validation is done in EARs or it could be intended because theoretically from WAR archive you can see two beans of type ServiceConnector.

As for how to workaround this - one thing I can think of are CDI extensions which (in Weld's interpretation) span across the whole EAR. Thefore using a CDI extension to register a bean might work for you regardless of what archive does that. If you go that way, look at AfterBeanDiscovery observer and register the bean there.

Siliarus
  • 6,393
  • 1
  • 14
  • 30