55import static org .jboss .jandex .Type .Kind .CLASS ;
66import static org .jboss .jandex .Type .Kind .PARAMETERIZED_TYPE ;
77import static org .jboss .jandex .Type .Kind .PRIMITIVE ;
8+ import static org .jboss .jandex .Type .Kind .TYPE_VARIABLE ;
89import static org .jboss .resteasy .reactive .client .impl .RestClientRequestContext .DEFAULT_CONTENT_TYPE_PROP ;
910import static org .jboss .resteasy .reactive .common .processor .EndpointIndexer .extractProducesConsumesValues ;
1011import static org .jboss .resteasy .reactive .common .processor .JandexUtil .isAssignableFrom ;
8384import org .jboss .jandex .ParameterizedType ;
8485import org .jboss .jandex .PrimitiveType ;
8586import org .jboss .jandex .Type ;
87+ import org .jboss .jandex .TypeVariable ;
8688import org .jboss .logging .Logger ;
8789import org .jboss .resteasy .reactive .client .api .ClientMultipartForm ;
8890import org .jboss .resteasy .reactive .client .handlers .ClientObservabilityHandler ;
@@ -930,7 +932,7 @@ A more full example of generated client (with sub-resource) can is at the bottom
930932 handleSubResourceMethod (enrichers , generatedClasses , interfaceClass , index , defaultMediaType ,
931933 httpAnnotationToMethod , name , classContext , baseTarget , methodIndex , method ,
932934 javaMethodParameters , jandexMethod , multipartResponseTypes , Collections .emptyList (),
933- generatedSubResources );
935+ generatedSubResources , new HashMap <>() );
934936 } else {
935937 FieldDescriptor methodField = classContext .createJavaMethodField (interfaceClass , jandexMethod ,
936938 methodIndex );
@@ -1228,7 +1230,7 @@ A more full example of generated client (with sub-resource) can is at the bottom
12281230 handleReturn (interfaceClass , defaultMediaType , method .getHttpMethod (),
12291231 method .getConsumes (), jandexMethod , methodCreator , formParams ,
12301232 bodyParameterIdx == null ? null : methodCreator .getMethodParam (bodyParameterIdx ), builder ,
1231- multipart );
1233+ multipart , Collections . emptyMap () );
12321234 }
12331235 }
12341236
@@ -1399,9 +1401,48 @@ private void handleSubResourceMethod(List<JaxrsClientReactiveEnricherBuildItem>
13991401 ClassRestClientContext ownerContext , ResultHandle ownerTarget , int methodIndex ,
14001402 ResourceMethod method , String [] javaMethodParameters , MethodInfo jandexMethod ,
14011403 Set <ClassInfo > multipartResponseTypes , List <SubResourceParameter > ownerSubResourceParameters ,
1402- Map <GeneratedSubResourceKey , String > generatedSubResources ) {
1404+ Map <GeneratedSubResourceKey , String > generatedSubResources , Map <String , Type > ownerIdentifierToTypeVariable ) {
1405+
1406+ Map <String , Type > identifierToTypeVariable = new HashMap <>();
14031407 Type returnType = jandexMethod .returnType ();
1404- if (returnType .kind () != CLASS ) {
1408+ if (returnType .kind () == PARAMETERIZED_TYPE ) {
1409+
1410+ ParameterizedType parameterizedReturnType = returnType .asParameterizedType ();
1411+ ClassInfo returnClass = index .getClassByName (returnType .name ());
1412+ ParameterizedType .Builder methodReturnTypeBuilder = ParameterizedType .builder (returnType .name ());
1413+ for (int i = 0 ; i < parameterizedReturnType .arguments ().size (); i ++) {
1414+ Type paramReturnTypeArg = parameterizedReturnType .arguments ().get (i );
1415+ Type resolvedType ;
1416+ if (paramReturnTypeArg .kind () == TYPE_VARIABLE ) {
1417+ // method returns another subresource, and one of the arguments is a type variable e.g. Wrapper<T>
1418+ resolvedType = ownerIdentifierToTypeVariable .get (paramReturnTypeArg .asTypeVariable ().identifier ());
1419+ if (resolvedType == null ) {
1420+ throw new IllegalArgumentException (
1421+ "Type variable %s of the sub resource locator method's return type %s could not be resolved."
1422+ .formatted (paramReturnTypeArg .asTypeVariable ().identifier (), jandexMethod ));
1423+ }
1424+ } else {
1425+ // Subresource, but no type variable, e.g. Wrapper<String>
1426+ resolvedType = paramReturnTypeArg ;
1427+ }
1428+
1429+ identifierToTypeVariable .put (returnClass .typeParameters ().get (i ).identifier (), resolvedType );
1430+ methodReturnTypeBuilder .addArgument (resolvedType );
1431+ }
1432+
1433+ // rewrite returnType to reflect the resolved type variable for the generatedSubResources cache
1434+ // i.e. Wrapper<String> instead of Wrapper<V>
1435+ returnType = methodReturnTypeBuilder .build ();
1436+ } else if (returnType .kind () == TYPE_VARIABLE ) {
1437+ TypeVariable typeVariable = returnType .asTypeVariable ();
1438+ // rewrite returnType to reflect the resolved type variable for the generatedSubResources cache
1439+ // i.e. String instead of Type Variable V
1440+ returnType = identifierToTypeVariable .get (typeVariable .identifier ());
1441+ if (returnType == null ) {
1442+ return ;
1443+ }
1444+
1445+ } else if (returnType .kind () != CLASS ) {
14051446 // sort of sub-resource method that returns a thing that isn't a class
14061447 throw new IllegalArgumentException ("Sub resource type is not a class: " + returnType .name ().toString ());
14071448 }
@@ -1854,7 +1895,7 @@ private void handleSubResourceMethod(List<JaxrsClientReactiveEnricherBuildItem>
18541895 handleReturn (subInterface , defaultMediaType ,
18551896 getHttpMethod (jandexSubMethod , subMethod .getHttpMethod (), httpAnnotationToMethod ),
18561897 consumes , jandexSubMethod , subMethodCreator , formParams , bodyParameterValue ,
1857- builder , multipart );
1898+ builder , multipart , identifierToTypeVariable );
18581899 } else {
18591900 // finding corresponding jandex method, used by enricher (MicroProfile enricher stores it in a field
18601901 // to later fill in context with corresponding java.lang.reflect.Method)
@@ -1867,7 +1908,7 @@ private void handleSubResourceMethod(List<JaxrsClientReactiveEnricherBuildItem>
18671908 handleSubResourceMethod (enrichers , generatedClasses , subInterface , index ,
18681909 defaultMediaType , httpAnnotationToMethod , subName , subContext , subMethodTarget ,
18691910 subMethodIndex , subMethod , subJavaMethodParameters , jandexSubMethod ,
1870- multipartResponseTypes , subParamFields , generatedSubResources );
1911+ multipartResponseTypes , subParamFields , generatedSubResources , identifierToTypeVariable );
18711912 }
18721913
18731914 }
@@ -2295,13 +2336,24 @@ private String getHttpMethod(MethodInfo subMethod, String defaultMethod, Map<Dot
22952336
22962337 private void handleReturn (ClassInfo restClientInterface , String defaultMediaType , String httpMethod , String [] consumes ,
22972338 MethodInfo jandexMethod , MethodCreator methodCreator , ResultHandle formParams ,
2298- ResultHandle bodyValue , AssignableResultHandle builder , boolean multipart ) {
2339+ ResultHandle bodyValue , AssignableResultHandle builder , boolean multipart ,
2340+ Map <String , Type > identifierToTypeVariable ) {
22992341 Type returnType = jandexMethod .returnType ();
23002342 ReturnCategory returnCategory = ReturnCategory .BLOCKING ;
23012343
2344+ if (returnType .kind () == TYPE_VARIABLE ) {
2345+ TypeVariable typeVariable = returnType .asTypeVariable ();
2346+ Type resolvedTypeVariable = identifierToTypeVariable .get (typeVariable .identifier ());
2347+ if (resolvedTypeVariable != null ) {
2348+ returnType = resolvedTypeVariable ;
2349+ } else {
2350+ throw new RuntimeException ("Type variable %s of the return type of method %s could not be resolved."
2351+ .formatted (typeVariable .identifier (), jandexMethod ));
2352+ }
2353+ }
2354+
23022355 String simpleReturnType = returnType .name ().toString ();
23032356 ResultHandle genericReturnType = null ;
2304-
23052357 if (returnType .kind () == PARAMETERIZED_TYPE ) {
23062358 ParameterizedType paramType = returnType .asParameterizedType ();
23072359 if (ASYNC_RETURN_TYPES .contains (paramType .name ())) {
0 commit comments