2121import feign .codec .Decoder ;
2222import feign .codec .Encoder ;
2323import feign .codec .ErrorDecoder ;
24+
25+ import java .lang .reflect .Method ;
26+ import java .lang .reflect .ParameterizedType ;
27+ import java .lang .reflect .Type ;
28+ import java .lang .reflect .WildcardType ;
2429import java .util .concurrent .CompletableFuture ;
2530import java .util .concurrent .ExecutorService ;
2631import java .util .concurrent .Executors ;
4247 * be done (for example, creating and submitting a task to an {@link ExecutorService}).
4348 */
4449@ Experimental
45- public abstract class AsyncFeign <C > {
50+ public final class AsyncFeign <C > {
4651 public static <C > AsyncBuilder <C > builder () {
4752 return new AsyncBuilder <>();
4853 }
@@ -212,14 +217,14 @@ public AsyncFeign<C> build() {
212217 errorDecoder , methodHandlerFactory );
213218 final ReflectiveFeign <C > feign =
214219 new ReflectiveFeign <>(handlersByName , invocationHandlerFactory , queryMapEncoder );
215- return new ReflectiveAsyncFeign <>(feign , defaultContextSupplier );
220+ return new AsyncFeign <>(feign , defaultContextSupplier );
216221 }
217222 }
218223
219224 private final ReflectiveFeign <C > feign ;
220225 private final AsyncContextSupplier <C > defaultContextSupplier ;
221226
222- protected AsyncFeign (ReflectiveFeign <C > feign , AsyncContextSupplier <C > defaultContextSupplier ) {
227+ private AsyncFeign (ReflectiveFeign <C > feign , AsyncContextSupplier <C > defaultContextSupplier ) {
223228 this .feign = feign ;
224229 this .defaultContextSupplier = defaultContextSupplier ;
225230 }
@@ -229,8 +234,45 @@ public <T> T newInstance(Target<T> target) {
229234 }
230235
231236 public <T > T newInstance (Target <T > target , C context ) {
232- return wrap (target .type (), feign .newInstance (target , context ), context );
237+ verifyTargetSpecfication (target );
238+ return feign .newInstance (target , context );
239+ }
240+
241+ private <T > void verifyTargetSpecfication (Target <T > target ) {
242+ Class <T > type = target .type ();
243+ if (!type .isInterface ()) {
244+ throw new IllegalArgumentException ("Type must be an interface: " + type );
245+ }
246+
247+ for (final Method m : type .getMethods ()) {
248+ final Class <?> retType = m .getReturnType ();
249+
250+ if (!CompletableFuture .class .isAssignableFrom (retType )) {
251+ continue ; // synchronous case
252+ }
253+
254+ if (retType != CompletableFuture .class ) {
255+ throw new IllegalArgumentException ("Method return type is not CompleteableFuture: "
256+ + getFullMethodName (type , retType , m ));
257+ }
258+
259+ final Type genRetType = m .getGenericReturnType ();
260+
261+ if (!ParameterizedType .class .isInstance (genRetType )) {
262+ throw new IllegalArgumentException ("Method return type is not parameterized: "
263+ + getFullMethodName (type , genRetType , m ));
264+ }
265+
266+ if (WildcardType .class
267+ .isInstance (ParameterizedType .class .cast (genRetType ).getActualTypeArguments ()[0 ])) {
268+ throw new IllegalArgumentException (
269+ "Wildcards are not supported for return-type parameters: "
270+ + getFullMethodName (type , genRetType , m ));
271+ }
272+ }
233273 }
234274
235- protected abstract <T > T wrap (Class <T > type , T instance , C context );
275+ private String getFullMethodName (Class <?> type , Type retType , Method m ) {
276+ return retType .getTypeName () + " " + type .toGenericString () + "." + m .getName ();
277+ }
236278}
0 commit comments