UnsatisfiedResolutionException thrown when using Spring Data Elasticsearch in CDI

576 Views Asked by At

I followed the Spring Data Elasticsearch documentation with regard to the CDI integration in order to use Elasticsearch repositories in a CDI environment.

In short I have imported the related dependencies with Maven and tried to make available the ElasticsearchOperations as a bean.

The result is an UnsatisfiedResolutionException thrown by the ElasticsearchRepositoryExtension when trying to initialize potentional Elasticsearch repositories. Probably the ElasticsearchOperations bean is not initialized prior to the execution of the ElasticsearchRepositoryExtension.

Is this a bug or a misconfiguration?

  • JDK 8
  • Java EE 7
  • Wildfly 8.1.0

Maven dependency

   <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-elasticsearch</artifactId>
        <version>1.1.0.RELEASE</version>
   </dependency>

Java code

    @Produces
    @ApplicationScoped
    public ElasticsearchOperations createElasticsearchTemplate() {
        return new ElasticsearchTemplate(NodeBuilder.nodeBuilder().local(true).node().client());
    }

Stacktrace

11:13:32,658 ERROR [org.jboss.as.server] (management-handler-thread - 1) JBAS015860: Redeploy of deployment "application.war" was rolled back with the following failure message:
{"JBAS014671: Failed services" => {"jboss.deployment.unit.\"application.war\".WeldStartService" => "org.jboss.msc.service.StartException in service jboss.deployment.unit.\"application.war\".WeldStartService: Failed to start service
    Caused by: org.jboss.weld.exceptions.DefinitionException: Exception List with 1 exceptions:
Exception 0 :
javax.enterprise.inject.UnsatisfiedResolutionException: Unable to resolve a bean for 'org.springframework.data.elasticsearch.core.ElasticsearchOperations' with qualifiers [@javax.enterprise.inject.Default(), @javax.enterprise.inject.Any()].
        at org.springframework.data.elasticsearch.repository.cdi.ElasticsearchRepositoryExtension.createRepositoryBean(ElasticsearchRepositoryExtension.java:76)
        at org.springframework.data.elasticsearch.repository.cdi.ElasticsearchRepositoryExtension.afterBeanDiscovery(ElasticsearchRepositoryExtension.java:63)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:483)
        at org.jboss.weld.injection.MethodInjectionPoint.invokeOnInstanceWithSpecialValue(MethodInjectionPoint.java:93)
        at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:266)
        at org.jboss.weld.event.ExtensionObserverMethodImpl.sendEvent(ExtensionObserverMethodImpl.java:125)
        at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:253)
        at org.jboss.weld.event.ObserverMethodImpl.notify(ObserverMethodImpl.java:232)
        at org.jboss.weld.event.ObserverNotifier.notifyObserver(ObserverNotifier.java:169)
        at org.jboss.weld.event.ObserverNotifier.notifyObservers(ObserverNotifier.java:128)
        at org.jboss.weld.event.ObserverNotifier.fireEvent(ObserverNotifier.java:102)
        at org.jboss.weld.bootstrap.events.AbstractContainerEvent.fire(AbstractContainerEvent.java:63)
        at org.jboss.weld.bootstrap.events.AbstractDefinitionContainerEvent.fire(AbstractDefinitionContainerEvent.java:35)
        at org.jboss.weld.bootstrap.events.AfterBeanDiscoveryImpl.fire(AfterBeanDiscoveryImpl.java:55)
        at org.jboss.weld.bootstrap.WeldStartup.deployBeans(WeldStartup.java:372)
        at org.jboss.weld.bootstrap.WeldBootstrap.deployBeans(WeldBootstrap.java:79)
        at org.jboss.as.weld.WeldStartService.start(WeldStartService.java:92)
        at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1948)
        at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1881)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
3

There are 3 best solutions below

1
On

Does the class containing your producer method have a bean-defining annotation (e.g. @Dependent)? If not, the producer method will be ignored by CDI 1.1.

0
On

I've done some research myself and have found what is going wrong.

The ElasticsearchOperations bean is picked from CDI(actually from the Elasticsearch CDI extension) and put in a map with key the bean qualifiers (by default @Any and @Default). The problem here is that the toString() method is called before setting the key which makes the order of the qualifiers to be hardcoded in the key.

At a later stage a repository bean is discovered having the same qualifiers in different order (@Default and @Any). Thus, it is impossible to get the ElasticsearchOperations from the map in order to associate it with the repository.

I managed to overcome this problem by setting a custom qualifier in both the repository and the ElasticsearchOperations producer method. By doing so the order of the qualifiers is the same now. But then another issue occured...

Its time to mention that I also use the Spring Data JPA and the respective CDI extension. The problem now is that both extension process each repository bean (regardless of what Repostiory interface they implement) resulting in registering the same bean twice which is not acceptable.

0
On

Spring Data CDI extensions are all broken because of a weld bug: https://issues.jboss.org/browse/WELD-2185

Weld uses ArraySet to store qualifiers in a Bean. Extensions that store qualifiers to a Bean and register custom Bean instances based on ProcessAnnotatedType with qualifiers are no longer able to use simple matching to obtain the Bean by its qualifiers.

I have fixed it temporarily by changing the way all qualifiers are set in the CdiRepositoryExtension class.

                Set<Annotation> qualifiers = bean.getQualifiers().stream()
                    .sorted((e1, e2) -> Integer.compare(e1.hashCode(),
                            e2.hashCode())).collect(Collectors.toSet());

For Spring Data Couchbase the full code is available here: https://github.com/ldoguin/wildfly-quickstart-spring-data/blob/master/src/main/java/org/springframework/data/couchbase/repository/cdi/CouchbaseRepositoryExtension.java