/* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ratpack.retrofit.internal; import com.google.common.reflect.TypeToken; import ratpack.exec.Promise; import ratpack.http.client.ReceivedResponse; import ratpack.retrofit.RatpackRetrofitCallException; import ratpack.util.Exceptions; import retrofit2.*; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public class RatpackCallAdapterFactory extends CallAdapter.Factory { public static final RatpackCallAdapterFactory INSTANCE = new RatpackCallAdapterFactory(); private RatpackCallAdapterFactory() { } @Override public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { TypeToken<?> rawType = TypeToken.of(returnType); if (rawType.getRawType() != Promise.class) { return null; } if (!(returnType instanceof ParameterizedType)) { throw new IllegalStateException("Promise return type must be parameterized" + " as Promise<Foo> or Promise<? extends Foo>"); } return getCallAdapter((ParameterizedType) returnType); } // returnType is the parameterization of Promise protected CallAdapter<Promise<?>> getCallAdapter(ParameterizedType returnType) { Type parameterType = Utils.getSingleParameterUpperBound(returnType); TypeToken<?> parameterTypeToken = TypeToken.of(parameterType); //Promising a Response type, need the actual value if (parameterTypeToken.getRawType() == Response.class) { if (!(parameterType instanceof ParameterizedType)) { throw new IllegalStateException("Response return type must be parameterized" + " as Response<Foo> or Response<? extends Foo>"); } Type responseType = Utils.getSingleParameterUpperBound((ParameterizedType) parameterType); return new ResponseCallAdapter(responseType); } else if (parameterTypeToken.getRawType() == ReceivedResponse.class) { return new ReceivedResponseCallAdapter(parameterType); } //Else we're just promising a value return new SimpleCallAdapter(parameterType); } static final class ReceivedResponseCallAdapter implements CallAdapter<Promise<?>> { private final Type responseType; ReceivedResponseCallAdapter(Type responseType) { this.responseType = responseType; } @Override public Type responseType() { return responseType; } @Override public <R> Promise<ReceivedResponse> adapt(Call<R> call) { return new RatpackCallFactory.RatpackCall(call.request()).promise(); } } static final class ResponseCallAdapter implements CallAdapter<Promise<?>> { private final Type responseType; ResponseCallAdapter(Type responseType) { this.responseType = responseType; } @Override public Type responseType() { return responseType; } @Override public <R> Promise<Response<?>> adapt(Call<R> call) { return Promise.async(downstream -> call.enqueue(new Callback<R>() { @Override public void onResponse(Call<R> call, Response<R> response) { downstream.success(response); } @Override public void onFailure(Call<R> call, Throwable t) { downstream.error(t); } }) ); } } static final class SimpleCallAdapter implements CallAdapter<Promise<?>> { private final Type responseType; SimpleCallAdapter(Type responseType) { this.responseType = responseType; } @Override public Type responseType() { return responseType; } @Override public <R> Promise<?> adapt(Call<R> call) { return Promise.async(downstream -> call.enqueue(new Callback<R>() { @Override public void onResponse(Call<R> call, Response<R> response) { if (response.isSuccessful()) { downstream.success(response.body()); } else { Exceptions.uncheck(() -> downstream.error(RatpackRetrofitCallException.cause(call, response)) ); } } @Override public void onFailure(Call<R> call, Throwable t) { downstream.error(t); } }) ); } } }