Map containing Retrofit API interface method from ServiceHelper - java

I am trying to follow the REST client implementation pattern described in the Google I/O Dobjanschi video here and am using Retrofit2 for the REST API calls.
Based on the REST client pattern described above I introduced a ServiceHelper layer that calls the actual API method via Retrofit. However I don't have a clean way to call the interface methods from the ServiceHelper layer.
I currently have an enum of the available API calls and pass that from the ServiceHelper. And in my ApiProcessor introduced a function that uses an giant if..else if ladder that returns the appropriate Retrofit API interface call based on the enum passed in. I haven't really found a better/cleaner approach to this.
Is there a better / cleaner way to map these? Or any other ideas to do this?

You should throw away that monolithic ServiceHelper and create several repositories following the repository pattern in order to encapsulate and distribute responsibilities between classes.
Actually, the Retrofit API itself favors composition over inheritance, so you can easily create as much interfaces as needed and use them in the right repository.

Without the code it is a bit hard to "inspect" your solution. :)
As You asked the question it is not really the best way to solve the problem in this way (in my opinion). Altough there are a ton approaches like "if it works it is OK".
In my opinion a bit cleaner solution would be the following: Your helper is a good thing. It should be used to hide all the details of the API You are using.
It is a good thing to hide those API specific stuff because if it changes You only forced to change only your helper/adapter. My recommendation is to use multiple method in apiprocessor and not enums. It is a bit easier to maintain and fix if something is changing. Plus you do not have to take care of your Enum.
TLDR: If it works probably it is OK. You do not have to write million dollar production code to test something, but if you would like to get a good habit You should consider the refactor that code into separate processor methods.

You Can follow service pattern:
a) Create resource interface which are your exposed rest methods eg:
public interface AppResource {
#Headers({"Accept: application/json", "Content-Type: application/json"})
#GET(ApiConstants.API_VERSION_V1 + "/users")
Call<List<User>> getUsers();
}
b) Create RetrofitFactory
public class RetrofitFactory {
private static Retrofit userRetrofit;
#NonNull
private static Retrofit initRetrofit(String serverUrl) {
final HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// set your desired log level
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
final Request original = chain.request();
final Request request = original.newBuilder()
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
})
.addInterceptor(logging)
.build();
return new Retrofit.Builder()
.baseUrl(serverUrl)
.addConverterFactory(JacksonConverterFactory.create())
.client(okHttpClient)
.build();
}
public static Retrofit getUserRetrofit() {
if (userRetrofit == null) {
final String serverUrl = context.getString(R.string.server_url); //Get context
userRetrofit = initRetrofit(serverUrl);
}
return userRetrofit;
}
}
c) Create a abstract BaseService which your every service will extend
public abstract class BaseService<Resource> {
protected final Resource resource;
final Retrofit retrofit;
public BaseService(Class<Resource> clazz) {
this(clazz, false);
}
public BaseService(Class<Resource> clazz) {
retrofit = RetrofitFactory.getUserRetrofit();
resource = retrofit.create(clazz);
}
protected <T> void handleResponse(Call<T> call, final ResponseHandler<T> responseHandler) {
call.enqueue(new Callback<T>() {
#Override
public void onResponse(final Call<T> call, final Response<T> response) {
if (response.isSuccess()) {
if (responseHandler != null) {
responseHandler.onResponse(response.body());
}
} else {
final ErrorResponse errorResponse = parseError(response);
if (responseHandler != null) {
responseHandler.onError(errorResponse);
}
}
}
#Override
public void onFailure(final Call<T> call, final Throwable throwable) {
if (responseHandler != null) {
responseHandler.onFailure(throwable);
}
}
});
}
}
d) Now your user service with their response handler
public interface UserService {
void getUsers(ResponseHandler<List<User>> userListResponse);
}
e) Now your user service implementation class which extends baseservice
public class UserServiceImpl extends BaseService<UserResource> implements UserService {
public UserServiceImpl () {
super(AppResource.class);
}
#Override
public void getUsers(ResponseHandler<List<User>> userListResponse) throws UserServiceException {
final Call<List<User>> response = resource.getUsers();
handleResponse(response, userListResponse);
}
f) Create a service factory which you will reuse to call services eg:
public class ServiceFactory {
private static UserService userservice;
UserService getUserService(){
if (UserService == null) {
UserService = new UserServiceImpl();
}
return UserService ;
}
g) Now simply call service and pass your response handler
ServiceFactory.getUserService().getUsers(getUserListResponseHandler());
} catch (your exception handler) {
//log your excp
}

Related

Is there a way to get request URI in Spring?

Whenever a request is made, I need to get the request URI for some internal calculations.
For some time I've been doing it like this:
public Mono<Response> example(ServerHttpRequest req) { ... }
And then using req.getURI(), but that becomes a pain once you need to pass it down multiple times. I need the URI object to extract scheme, schemeSpecificPart, host, port from it.
Is there a way to get these properties without extracting them from a request?
UPD: I see that for Web MVC there are convenient methods to retrieve request URI. But I need the same for reactive stack (netty).
It can be achieved by creating WebFilter that puts ServerHttpRequest into the Context:
#Component
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveRequestContextFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
return chain
.filter(exchange)
.contextWrite(ctx -> ctx.put(ReactiveRequestContextHolder.CONTEXT_KEY, request));
}
}
Additionally, create a class that provides static access to request data:
public class ReactiveRequestContextHolder {
public static final Class<ServerHttpRequest> CONTEXT_KEY = ServerHttpRequest.class;
public static Mono<ServerHttpRequest> getRequest() {
return Mono.deferContextual(Mono::just).map(ctx -> ctx.get(CONTEXT_KEY));
}
public static Mono<URI> getURI() {
return getRequest().map(HttpRequest::getURI);
}
}
Methods can be accessed through the class name directly without having to instantiate them. Just be aware that it should not be accessed before the filter is executed.
Example of usage:
#RestController
#RequestMapping
public class TestController {
#GetMapping("/test")
public Mono<URI> test() {
return ReactiveRequestContextHolder.getURI();
}
}
Reference
You can try this :
public Mono<Response> example(WebRequest request){
System.out.println(request.getDescription(false));
.......
}
You can turn this false to true in getDescription as false will only give you the Uri which i think is the only thing you need.
You can inject it in any bean.
#Autowired
private HttpServletRequest request;

How to access OAuth2AuthorizedClient in Webflux Functional Endpoints?

With the classic MVC-style RestController it's easy to get the OAuth2AuthorizedClient, all I need to do is this:
#GetMapping("/foo")
public Foo getFoo(#RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient client){
return new Foo(client.getPrincipalName());
}
With Webflux's functional endpoints, however, how can I access the OAuth2AuthorizedClient in a similar fashion?
EDIT:
To make it more clear, I'm aware that the reactive approach works the same as the non-reactive one. What I'm curious about is how I can access the OAuth2AuthorizedClient from Webflux's functional endpoints:
PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);
RouterFunction<ServerResponse> route = route()
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
public class PersonHandler {
// ...
public Mono<ServerResponse> listPeople(ServerRequest request) {
// ...
}
public Mono<ServerResponse> createPerson(ServerRequest request) {
// ...
}
public Mono<ServerResponse> getPerson(ServerRequest request) {
// ...
}
}
For example, in the createPerson method, how do I access OAuth2AuthorizedClient?
Found a solution!
#Component
#RequiredArgsConstructor
public class OAuth2Utils {
private final ServerOAuth2AuthorizedClientRepository authorizedClientRepository;
public Mono<OAuth2AuthorizedClient> extractOAuth2AuthorizedClient(ServerRequest request) {
return request.principal()
.filter(principal -> principal instanceof OAuth2AuthenticationToken)
.cast(OAuth2AuthenticationToken.class)
.flatMap(auth -> authorizedClientRepository.loadAuthorizedClient(auth.getAuthorizedClientRegistrationId(), auth, request.exchange()));
}
}
Looks like it works the same as in non reactive approach.
Please take a look on:
docs:
https://docs.spring.io/spring-security/site/docs/5.1.0.RELEASE/reference/html/webflux-roac.html
example: https://github.com/spring-projects/spring-security/tree/5.1.0.RELEASE/samples/boot/oauth2webclient-webflux

wrap a SOAP call (WebServiceGatewaySupport) with Hystrix

I am trying to find out an example to use hystrix with SOAP call and all i could find the examples of the same with REST.
From the hystrix documentation, it seems this is possible, if you could point me to the example that would be helpful.
Also, if there are any better ways of having a consistent circuit breaker between REST and SOAP calls (maybe extensible to EJB's).
You can do this by creating an inner class which extends HystrixCommand and then override the run() method.
public class webServiceClient extends WebServiceGatewaySupport {
public Response callsoap(Request request) {
SoapCommand sfc = new SoapCommand(getWebServiceTemplate(), request,
soapRequestHeaderModifier, configuration);
return sfc.execute();
}
class SoapCommand extends HystrixCommand<Response>{
public SoapCommand() {
super(HystrixCommandGroupKey.Factory.asKey("example"));
}
#Override
protected Response run() {
return (Response) webServiceTemplate.marshalSendAndReceive(configuration.getUri(),
request, soapRequestHeaderModifier);
}
//fallback method goes here
}
}

Calling Spring controller method without going to internet

tldr: Is there a way to make an internal request (using the method's path) without going to the internet?
--
Why do I need it? I have a project which receives many events. The decision of who will handle each event is made by a Controller. So I have something similar to this:
#RestController
#RequestMapping("/events")
public class EventHandlerAPI {
#Autowired
private EventAHandler eventAhandler;
#Autowired
private EventBHandler eventBhandler;
#PostMapping("/a")
public void handleEventA(#RequestBody EventA event) {
eventAhandler.handle(id, event);
}
#PostMapping("/b")
public void handleEventB(#RequestBody EventB event) {
eventBhandler.handle(id, event);
}
}
We recently added support to receive events through a Queue service. It sends to us the payload and the event class. Our decision is to let both interfaces working (rest and queue). The solution to avoid code duplication was to keep the Controller choosing which handler will take care of the event. The code nowadays is similar to this:
#Configuration
public class EventHandlerQueueConsumer {
#Autowired
private EventHandlerAPI eventHandlerAPI;
private Map<Class, EventHandler> eventHandlers;
#PostConstruct
public void init() {
/* start listen queue */
declareEventHandlers();
}
private void declareEventHandlers() {
eventHandlers = new HashMap<>();
eventHandlers.put(EventAHandler.class, (EventHandler<EventAHandler>) eventHandlerAPI::handleEventA);
eventHandlers.put(EventBHandler.class, (EventHandler<EventBHandler>) eventHandlerAPI::handleEventB);
}
private void onEventReceived(AbstractEvent event) {
EventHandler eventHandler = eventHandlers.get(event.getClass());
eventHandler.handle(event);
}
private interface EventHandler<T extends AbstractEvent> {
void handle(T event);
}
}
This code works, but it doesn't let the controller choose who will handle the event (our intention). The decision is actually being made by the map.
What I would like to do was to invoke the controller method through it's request mapping without going to the internet. Something like this:
#Configuration
public class EventHandlerQueueConsumer {
// MADE UP CLASS TO SHOW WHAT I WANT
#Autowired
private ControllerInkover controllerInvoker;
#PostConstruct
public void init() { /* start listen queue */ }
private void onEventReceived(AbstractEvent event) {
controllerInvoker.post(event.getPath(), new Object[] { event });
}
}
This way is much cleaner and let all the decisions be made by the controller.
I've researched a lot and didn't found a way to implement it. Debugging spring, I found how he routes the request after the DispatcherServlet, but all the spring internals uses HttpServletRequest and HttpServletResponse :(
Is there a way to make an internal request (using the method's path) without going to the internet?
They are classes of the same application
Then it should easy enough.
1) You can call your own API on http(s)://localhost:{port}/api/{path} using RestTemplate utility class. This is preferred way, since you'll follow standard MVC pattern. Something like:
restTemplate.exchange(uri, HttpMethod.POST, httpEntity, ResponseClass.class);
2) If you don't want to invoke network connection at all, then you can either use Spring's internal to find the mapping/method map or use some reflection to build custom
map upon controller's startup. Then you can pass your event/object to the method from the map in a way shown in your mock-up class. Something like:
#RequestMapping("foo")
public void fooMethod() {
System.out.println("mapping = " + getMapping("fooMethod")); // you can get all methods/mapping in #PostContruct initialization phase
}
private String getMapping(String methodName) {
Method methods[] = this.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName() == methodName) {
String mapping[] = methods[i].getAnnotation(RequestMapping.class).value();
if (mapping.length > 0) {
return mapping[mapping.length - 1];
}
}
}
return null;
}

Making http request using retrofit from non activity class

We have used a Activity-BL-DAO-DB(sqlite) in my app while developing.
Due to change in requirement, we have to use REST service from the server alone. I have seen Retrofit for it. But I'm not sure how to use it in DAO classes instead of SQL queries.
We have looked into bus concepts which requires more rework. We wanted to make minimal changes to the code to incorporate this change.
If anything else needed,let me know.
Eg:
Following is the sample flow which will display list of technologies in the list.
Technology Activity OnCreate method:
techList=new ArrayList<Technology>();
techList=technologyBL.getAllTechnology(appId);
adapterTech=new TechnologyAdapter(this,new ArrayList<Technology> (techList));
listView.setAdapter(adapterTech);
Technology BL :
public List<Technology> getAllTechnology(String appId) {
techList=technologyDao.getAllTechnology(appId);
// some logic
return techList;
}
Technology DAO:
public List<Technology> getAllTechnology(String appId) {
//sql queries
return techList;
}
Technology Model:
class Technology{
String id,techName,techDescription;
//getters & setters
}
I have to replace sql queries with retrofit request. I have created the following retrofit class and interfaces:
RestClient Interface:
public interface IRestClient {
#GET("/apps/{id}/technologies")
void getTechnoloies(#Path("id") String id,Callback<List<Technology>> cb);
//Remaining methods
}
RestClient :
public class RestClient {
private static IRestClient REST_CLIENT;
public static final String BASE_URL = "http://16.180.48.236:22197/api";
Context context;
static {
setupRestClient();
}
private RestClient() {}
public static RestClient get() {
return REST_CLIENT;
}
private static void setupRestClient() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(BASE_URL)
.setClient(getClient())
.setRequestInterceptor(new RequestInterceptor() {
//cache related things
})
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
REST_CLIENT = restAdapter.create(IAluWikiClient.class);
}
private static OkClient getClient(){
//cache related
}
}
I tried calling with both sync/async methods in DAO. For sync method,it was throwing some error related to main thread.For Async,it is crashing as request is done late.
Sync call in DAO:
techList=RestClient.get().getTecchnologies(id);
Async call in DAO:
RestClient.get().getTechnolgies(id,new CallBack<List<Technolgy>(){
#Override
public void success(List<Technology> technologies, Response response) {
techList=technologies;
}
#Override
public void failure(Retrofit error){}
});
You've got two options here.
The first is to create the Retrofit callback in the Activity:
RestClient.get().getTechnolgies(id,new CallBack<List<Technolgy>(){
#Override
public void success(List<Technology> technologies, Response response) {
ArrayList<Technology> techList = technologyBL.someLogic(technologies);
adapterTech=new TechnologyAdapter(this,techList);
listView.setAdapter(adapterTech);
}
#Override
public void failure(Retrofit error){}
});
Note that you will have to extract your //some logic part into a separate BL method.
The second option is to make the Retrofit API call return an RxJava Observable (which is integrated into Retrofit):
RestClient.get().getTechnolgies(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<Technology>>() {
#Override
public void call(List<Technology> technologies) {
ArrayList<Technology> techList = technologyBL.someLogic(technologies);
adapterTech=new TechnologyAdapter(this,techList);
listView.setAdapter(adapterTech);
}
});
In this case, your RestClient interface is:
public interface IRestClient {
#GET("/apps/{id}/technologies")
Observable<List<Technology>> getTechnologies(#Path("id") String id);
//Remaining methods
}
You can read more about it in the "SYNCHRONOUS VS. ASYNCHRONOUS VS. OBSERVABLE" section of http://square.github.io/retrofit/. Also, see these two blogposts to get your head around RxJava and Observables:
In my experience, I have found it useful to create a Service which executes calls to the Retrofit API by using custom AsyncTask implementations. This paradigm keeps all your data model interactions in one place (the service) and gets all the API calls off the main thread.

Categories

Resources