Call another Retrofit call on Subject emission - request

I have a following class:
public class SessionStore {
Subject<Session, Session> subject;
public SessionStore() {
subject = new SerializedSubject<>(BehaviorSubject.create(new Session());
}
public void set(Session session) {
subject.onNext(session);
}
public Observable<UserSession> observe() {
return subject.distinctUntilChanged();
}
}
In activity I observe the session and perform network operation on each change:
private Subscription init() {
return sessionStore
.observe()
.flatMap(new Func1<Session, Observable<Object>>() {
#Override
public Observable<Object> call(Session session) {
return (session.isValid()
? retrofitService.getThingForValid()
: retrofitService.getThingForInalid())
.subscribeOn(Schedulers.io());
}
})
.subscribe(...);
}
Now I have an Okhttp request interceptor, in which I set the session instance from valid to invalid when network response is non 200 code.
This is what happens:
On initial subscription to session store the getThingForValid() is executed, and fails.
OkHttp intercepts the fail and sets new session.
Session store emits a new, now invalid session.
The new emission executes a getThingForInvalid() method.
What is important to know is that this execution happens in the middle of the previous Retrofit call. This is because OkHttp client is wrapped by Retrofit and all interceptors are executed before Retrofit returns.
Having this in mind, you realize that the second call is being executed and processed by Retrofit already, while the first one hasn't finished yet.
As the first call finishes, it throws HttpException because response was non 200 code.
The xception kills the rx stream and with it the second call.
I have tried to ignore this exception in stream but the second call is cancelled by Retrofit anyways.
Do you have any ideas how to make my concept work please?

if you get response code 401 in case of token expiration:
you need to add Authenticator in to OkHttpClient.Builder
builder.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
final LoginResponse newLoginResponse = refreshTokenClient.refreshToken();
//save new token locally, if needed
return response
.request()
.newBuilder()
.removeHeader("Api-Auth-Token") // removing old header
.addHeader("Api-Auth-Token", newLoginResponse.getAuthToken())
.build();
}
});
where
public interface RefreshTokenService {
#PUT("/api/v1/tokens")
LoginResponse refreshToken();
}
But pay attention: this Authenticator will run each time when response code is 401.

Related

Controlling the reactor execution for certain use cases (or get response at certain point)

I am trying to update a document in MongoDB but cannot get to checking updated status and responding back to user. Below is my code:
#Autowired
ReactiveMongoTemplate mongoTemplate;
public Mono<String> updateUser(UserIn userIn) {
UserResponse resp = new UserResponse();
mongoTemplate.findAndModify(query, update, User.class)
//.doOnSuccess(bsItem -> {
.flatMap(user -> {
if(user.getItemId().equals(userIn.getId("_id")))
resp.setStatus("Updated");
else
resp.setStatus("Failed");
return Mono.just(resp);
}).subscribe();
return Mono.just(resp.getStatus());
}
Even though update is happening in mongodb, it throws NPE while returning. How to get the control after reactor operator is executed here?
You should almost never subscribe in your own application.
The subscriber is the client that initiated the call in this case it is probably the web application. You application is just relaying the data, so your application is a publisher which means you should not subscribe. The web app subscribes.
Try this.
#Autowired
ReactiveMongoTemplate mongoTemplate;
public Mono<String> updateUser(UserIn userIn) {
return mongoTemplate.findAndModify(query, update, User.class)
.flatMap(user -> {
final UserResponse resp = new UserResponse();
if(user.getItemId().equals(userIn.getId("_id")))
resp.setStatus("Updated");
else
resp.setStatus("Failed");
return Mono.just(resp.getStatus());
});
}
A mono is not like a stream, you fetch, map and return, all in the same mono, like a chain of events. An event chain.

AppEngine Objectify Java Unit Test Object Cache Error

AppEngine Objectify Java Unit Test gives a weird session cache error as described below.
Test Case:
private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
new LocalDatastoreServiceTestConfig());
protected Closeable session;
#Before
public void setUp() throws Exception {
helper.setUp();
ObjectifyService.setFactory(new ObjectifyFactory());
ObjectifyService.register(UserData.class);
session = ObjectifyService.begin();
}
#After
public void tearDown() throws Exception {
session.close();
helper.tearDown();
}
#Test
public void testUserDataQuery throws Exception {
...
saveUserData();
...
getUserData();
...
}
Call 1:
UserData saveUserData {
...
UserData userData = (UserData) ofy().load().key(key).now();
...
// UserData is modified, the modifications are not stored in datastore,
// as those are temporary.
return userData;
}
Call 2:
UserData getUserData {
...
UserData userData = (UserData) ofy().load().key(key).now();
...
// Return the datastore saved UserData object.
return userData;
}
When the unit test case is executed, the modification done in saveUserData call is seen in getUserData query. Even though ofy().load() has been called, the UserData is not loaded from datastore instead served from the cached entry.
I have tried ofy().clear() call to clear the session cache. This is not avoid the error in all the cases.
This is happening only in Unit Test environment rather than in development or production server.
In the code you posted, yes you'll get the same object back - that's the way the session cache works. Clearing the session cache after save will indeed give you a new object loaded from the datastore (or memcache). But my guess is that this is not what you really want to test.
I'm wildly guessing that you are trying to simulate multiple backend calls in your test. IRL, each backend call will operate in its own server-side context. So what I recommend is creating a context for each call, using closures:
#Test
public void testUserDataQuery throws Exception {
...
req(() -> saveUserData());
...
req(() -> getUserData());
...
}
Where req() does the begin()/close() of the Objectify context (as well as any other request-specific processing your container usually does). You can leave the Objectify initialization in the before/after.

Hystrix run() method executing even after timeout

I created a class by extending HystrixCommand and configured timeout for the request using below code
HystrixCommandProperties.Setter().withExecutionIsolationThreadTimeoutInMilliseconds(int milliSec);
Also overriden fallBack method when request not returns response within timeout specified
#Override
protected JSONObject run() throws Exception
{
// performed some http call
final HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders);
ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.GET, httpEntity, JSONObject.class)
.getBody();
System.out.println(response);
return response;
}
#Override
protected JSONObject getFallback()
{
final JSONObject json = new JSONObject();
json.put("status", "900");
json.put("message", "Request Failed");
return json;
}
Am getting fallBack method response whenever request takes more time than timeout specified which is expected.
But, what I observed is, when timeout occurs fallBack response is getting returned, and the response of the request which is executed in run method is coming later and am not able to hold or return as we returned the fallback response as soon as timeout.
Is there any way I can return that response ?
Thanks in advance.

Customizing LockedException of spring security to send new status code to angular.js

I am using spring 4 + hibernate 4 + spring security (RESTFull Webservice API) and angular at front. (Very new to this all).
My failure handler is as follows:
#Component
public class AuthFailure extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
If my request fails I get 401 response which is as expected. Now I want to add AccountLocked and CredentialsExpiredException functionality. I am setting "false" appropriately when I return "User". Again I do get response with status as 401.
return new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, AuthorityUtils.NO_AUTHORITIES);
As I always get 401 and I do not get any user object inside response(Or may be don't know how to get it), at front end I am unable to find whether it's due to bad credentials or account locked or credentials expired as I want to redirect to another page. I also tried to catch exceptions and tried to forward different statuses but does not seems to hit this code. I always get 401.
#ExceptionHandler(LockedException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ModelAndView handleLockedException(Exception e) {
logger.error("Exception occurred => " + e.getMessage());
return new ErrorResponse(HttpStatus.BAD_REQUEST.value(), e.getMessage(), "Spring security exception").asModelAndView();
}
Please help me - how shall I handle at angular end so that I can redirect to appropriate page?
I think solution was very simple, just did not pay attention to AuthFailure method parameters. Modified my AuthFailuar as follows:
#Component
public class AuthFailure extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
if (exception instanceof LockedException) {
response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
}else if {
....
} else {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
Now as I have given different status codes I can easily differentiate at the front. Hope for somebody this would be helpful.

Apache Camel: Response is get lost when reached explicitly on route

I am getting a strange situation at the code below which simply routes request to Google and returns response.
It works well but when I activate the line commented out as "//Activating this line causes empty response on browser" to print out returned response from http endpoint (Google), response is disappear, nothing is displayed on browser. I thought it might be related with input stream of http response which can be consumed only once and I activated Stream Caching on context but nothing changed.
Apache Camel version is 2.11.0
Any suggestions are greatly appreciated, thanks in advance.
public class GoogleCaller {
public static void main(String[] args) throws Exception {
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
public void configure() {
from("jetty:http://0.0.0.0:8081/myapp/")
.to("jetty://http://www.google.com?bridgeEndpoint=true&throwExceptionOnFailure=false")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
System.out.println("Response received from Google, is streamCaching = " + exchange.getContext().isStreamCaching());
System.out.println("----------------------------------------------IN MESSAGE--------------------------------------------------------------");
System.out.println(exchange.getIn().getBody(String.class));
System.out.println("----------------------------------------------OUT MESSAGE--------------------------------------------------------------");
//System.out.println(exchange.getOut().getBody(String.class)); //Activating this line causes empty response on browser
}
});
}
});
context.setTracing(true);
context.setStreamCaching(true);
context.start();
}
}
As you use a custom processor to process the message, you should keep it in mind the in message of the exchange has response message from the google, if you are using exchange.getOut(), camel will create a new empty out message for you and treat it as response message.
Because you don't set the out message body in the processor, it makes sense that you get the empty response in the browser.

Resources