11package io .quarkus .elasticsearch .javaclient .deployment ;
22
3- import io .quarkus .arc .deployment .AdditionalBeanBuildItem ;
3+ import java .lang .annotation .Annotation ;
4+ import java .util .HashSet ;
5+ import java .util .List ;
6+ import java .util .Set ;
7+ import java .util .function .Supplier ;
8+
9+ import jakarta .enterprise .inject .Default ;
10+ import jakarta .inject .Singleton ;
11+
12+ import org .elasticsearch .client .RestClient ;
13+ import org .jboss .jandex .AnnotationInstance ;
14+ import org .jboss .jandex .ClassType ;
15+ import org .jboss .jandex .DotName ;
16+
17+ import com .fasterxml .jackson .databind .ObjectMapper ;
18+
19+ import co .elastic .clients .elasticsearch .ElasticsearchAsyncClient ;
20+ import co .elastic .clients .elasticsearch .ElasticsearchClient ;
21+ import co .elastic .clients .transport .ElasticsearchTransport ;
22+ import io .quarkus .arc .ActiveResult ;
23+ import io .quarkus .arc .BeanDestroyer ;
24+ import io .quarkus .arc .deployment .BeanRegistrationPhaseBuildItem ;
25+ import io .quarkus .arc .deployment .SyntheticBeanBuildItem ;
26+ import io .quarkus .arc .processor .DotNames ;
427import io .quarkus .deployment .Feature ;
28+ import io .quarkus .deployment .annotations .BuildProducer ;
529import io .quarkus .deployment .annotations .BuildStep ;
30+ import io .quarkus .deployment .annotations .ExecutionTime ;
31+ import io .quarkus .deployment .annotations .Record ;
32+ import io .quarkus .deployment .builditem .CombinedIndexBuildItem ;
633import io .quarkus .deployment .builditem .FeatureBuildItem ;
734import io .quarkus .deployment .builditem .NativeImageFeatureBuildItem ;
835import io .quarkus .deployment .builditem .nativeimage .ReflectiveClassBuildItem ;
936import io .quarkus .deployment .builditem .nativeimage .ServiceProviderBuildItem ;
10- import io .quarkus .elasticsearch .javaclient .runtime .ElasticsearchJavaClientProducer ;
37+ import io .quarkus .elasticsearch .javaclient .runtime .ElasticsearchJavaClientRecorder ;
38+ import io .quarkus .elasticsearch .restclient .common .deployment .ElasticsearchClientProcessorUtil ;
39+ import io .quarkus .elasticsearch .restclient .common .runtime .ElasticsearchClientBeanUtil ;
40+ import io .quarkus .elasticsearch .restclient .lowlevel .deployment .ConfiguredElasticsearchLowLevelClientBuildItem ;
41+ import io .quarkus .elasticsearch .restclient .lowlevel .deployment .ElasticsearchBuildTimeConfig ;
42+ import io .quarkus .elasticsearch .restclient .lowlevel .deployment .ElasticsearchLowLevelClientReferenceBuildItem ;
43+ import io .smallrye .common .annotation .Identifier ;
1144
1245class ElasticsearchJavaClientProcessor {
1346
47+ private static final DotName ELASTICSEARCH_CLIENT = DotName .createSimple (ElasticsearchClient .class .getName ());
48+
1449 @ BuildStep
1550 FeatureBuildItem feature () {
1651 return new FeatureBuildItem (Feature .ELASTICSEARCH_JAVA_CLIENT );
1752 }
1853
19- @ BuildStep
20- AdditionalBeanBuildItem build () {
21- return AdditionalBeanBuildItem .unremovableOf (ElasticsearchJavaClientProducer .class );
22- }
23-
2454 @ BuildStep
2555 ServiceProviderBuildItem serviceProvider () {
2656 return new ServiceProviderBuildItem ("jakarta.json.spi.JsonProvider" ,
@@ -38,4 +68,120 @@ NativeImageFeatureBuildItem enableElasticsearchJavaClientFeature() {
3868 "io.quarkus.elasticsearch.javaclient.runtime.graalvm.ElasticsearchJavaClientFeature" );
3969 }
4070
71+ @ BuildStep
72+ public void collectJavaClientReferences (
73+ CombinedIndexBuildItem indexBuildItem ,
74+ BeanRegistrationPhaseBuildItem registrationPhase ,
75+ List <ElasticsearchLowLevelClientReferenceBuildItem > elasticsearchLowLevelClientReferenceBuildItems ,
76+ BuildProducer <ElasticsearchJavaClientReferenceBuildItem > references ) {
77+ Set <String > clientNames = new HashSet <>();
78+ for (String name : ElasticsearchClientProcessorUtil .collectReferencedClientNames (indexBuildItem , registrationPhase ,
79+ Set .of (ELASTICSEARCH_CLIENT ),
80+ Set .of ())) {
81+ references .produce (new ElasticsearchJavaClientReferenceBuildItem (name ));
82+ clientNames .add (name );
83+ }
84+ // Because we may have not discovered all the clients with ^ we just create for any lower level client we have:
85+ for (ElasticsearchLowLevelClientReferenceBuildItem item : elasticsearchLowLevelClientReferenceBuildItems ) {
86+ if (clientNames .add (item .getName ())) {
87+ references .produce (new ElasticsearchJavaClientReferenceBuildItem (item .getName ()));
88+ }
89+ }
90+ }
91+
92+ @ Record (ExecutionTime .RUNTIME_INIT )
93+ @ BuildStep
94+ void generateElasticsearchClientBeans (
95+ ElasticsearchJavaClientRecorder recorder ,
96+ ConfiguredElasticsearchLowLevelClientBuildItem lowLevelClientBuildItem ,
97+ ElasticsearchBuildTimeConfig config ,
98+ List <ElasticsearchJavaClientReferenceBuildItem > elasticsearchLowLevelClientReferenceBuildItems ,
99+ BuildProducer <SyntheticBeanBuildItem > producer ) {
100+ for (ElasticsearchJavaClientReferenceBuildItem buildItem : elasticsearchLowLevelClientReferenceBuildItems ) {
101+ String clientName = buildItem .getName ();
102+ if (!lowLevelClientBuildItem .getNames ().contains (clientName )) {
103+ throw new IllegalStateException ("Unable to locate the low-level client [" + clientName
104+ + "]. Impossible to create the transport for higher level Elasticsearch clients. " +
105+ "Make sure that the low-level client is correctly configured." );
106+ }
107+ produceTransportBean (clientName , recorder , producer );
108+ produceBlockingClientBean (clientName , recorder , producer );
109+ produceAsyncClientBean (clientName , recorder , producer );
110+ }
111+ }
112+
113+ private void produceTransportBean (String clientName , ElasticsearchJavaClientRecorder recorder ,
114+ BuildProducer <SyntheticBeanBuildItem > producer ) {
115+ producer .produce (createSyntheticBean (
116+ clientName ,
117+ ElasticsearchTransport .class ,
118+ Singleton .class ,
119+ ElasticsearchClientBeanUtil .isDefault (clientName ),
120+ recorder .checkActiveElasticsearchTransportSupplier (clientName ))
121+ .addInjectionPoint (ClassType .create (DotName .createSimple (RestClient .class )), qualifier (clientName ))
122+ .addInjectionPoint (ClassType .create (DotName .createSimple (ObjectMapper .class )))
123+ .createWith (recorder .elasticsearchTransportSupplier (clientName ))
124+ .destroyer (BeanDestroyer .AutoCloseableDestroyer .class )
125+ .done ());
126+ }
127+
128+ private void produceBlockingClientBean (String clientName , ElasticsearchJavaClientRecorder recorder ,
129+ BuildProducer <SyntheticBeanBuildItem > producer ) {
130+ producer .produce (createSyntheticBean (
131+ clientName ,
132+ ElasticsearchClient .class ,
133+ Singleton .class ,
134+ ElasticsearchClientBeanUtil .isDefault (clientName ),
135+ recorder .checkActiveElasticsearchTransportSupplier (clientName ))
136+ .createWith (recorder .blockingClientSupplier (clientName ))
137+ .addInjectionPoint (ClassType .create (DotName .createSimple (ElasticsearchTransport .class )), qualifier (clientName ))
138+ .destroyer (BeanDestroyer .AutoCloseableDestroyer .class )
139+ .done ());
140+ }
141+
142+ private void produceAsyncClientBean (String clientName , ElasticsearchJavaClientRecorder recorder ,
143+ BuildProducer <SyntheticBeanBuildItem > producer ) {
144+ producer .produce (createSyntheticBean (
145+ clientName ,
146+ ElasticsearchAsyncClient .class ,
147+ Singleton .class ,
148+ ElasticsearchClientBeanUtil .isDefault (clientName ),
149+ recorder .checkActiveElasticsearchTransportSupplier (clientName ))
150+ .createWith (recorder .asyncClientSupplier (clientName ))
151+ .addInjectionPoint (ClassType .create (DotName .createSimple (ElasticsearchTransport .class )), qualifier (clientName ))
152+ .destroyer (BeanDestroyer .AutoCloseableDestroyer .class )
153+ .done ());
154+ }
155+
156+ private static <T > SyntheticBeanBuildItem .ExtendedBeanConfigurator createSyntheticBean (String clientName ,
157+ Class <T > type ,
158+ Class <? extends Annotation > scope ,
159+ boolean defaultBean ,
160+ Supplier <ActiveResult > checkActiveSupplier ) {
161+ SyntheticBeanBuildItem .ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem
162+ .configure (type )
163+ .scope (scope )
164+ .unremovable ()
165+ .setRuntimeInit ()
166+ .checkActive (checkActiveSupplier )
167+ .startup ();
168+
169+ if (defaultBean ) {
170+ configurator .defaultBean ();
171+ configurator .addQualifier (Default .class );
172+ }
173+
174+ configurator .addQualifier ().annotation (DotNames .IDENTIFIER ).addValue ("value" , clientName ).done ();
175+
176+ return configurator ;
177+ }
178+
179+ private static AnnotationInstance qualifier (String clientName ) {
180+ if (clientName == null || ElasticsearchClientBeanUtil .isDefault (clientName )) {
181+ return AnnotationInstance .builder (Default .class ).build ();
182+ } else {
183+ return AnnotationInstance .builder (Identifier .class ).value (clientName ).build ();
184+ }
185+ }
186+
41187}
0 commit comments