The premise:
We use session tokens that expire after 3 hours
I have to use a special call called auth
Currently, when a session token expires, we do not preemptively refresh it, but instead attempt the call and if that call returns 401-UNAUTHORIZED we call auth and retry the call. It works, but it isn't pretty
We're using Retrofit1 and RxJava1 (updating is currently not an option)
I would like to change it up in such a way, where I would check whether I need to refresh the token BEFORE I make the call.
I want to achieve that by chaining Observables and sharing the Auth-Observable between calls
Think like this:
Check if the SessionToken is about to expire or already expired
Start Auth-Call (returns an Observable)
Chain my call into the Auth-Call Observable
when another call happens and sees that the sessiontoken is close to expiring, check if there is an ongoing Auth-Call. If there is, chain the new call into Auth-Call
1st question: Is this actually a smart idea?
2nd question: Is this actually possible?
3rd question: How the hell do I do this?
The trick to this was using a Singleton Observable
Observable<AuthResponse> mAuthSingleton = retrofit.authenticate()
.doOnNext( response -> saveSessionToken(response.getSessionToken())
.share();
Observable<AuthResponse> getAuthCallWhenNecessary(){
if(sessionToken == null ||sessionToken.isAboutToExpire())
return mAuthSingleton;
else
return Observable.just(new AuthResponse());
}
Observable<WhateverResponse> getWhatever(){
return getAuthCallWhenNecessary().flatMap(response -> retrofit.getWhatever());
}
your calls don't care whether the AuthResponse, they get when calling getAuthCallWhenNecessary, is a real one, they only care that they get a response-object from Auth
Here's my (kinda) documented work-sample: https://gist.github.com/TormundsMember/ebcb3782b14477a3d5b72e898929fe61
I think you can use the combinaison of repeatWhen operator and delay operator.
After the delay of token expiration , re-subscribe to your observable to get the new token.
Hope this helps.
Sorry for my english.
What I did in my case might be a bit overkill but it works - I wrapped all Retrofit interfaces with my own proxy class:
Create your own proxy of your Retrofit interface with Proxy.newProxyInstance
The proxy creates the Retrofit implementation with Retrofit.Builder()
In the proxy's public Object invoke(Object proxy, Method method, Object[] args) call the actual Retrofit implementation, before this call you can check if the token was valid or not, if not, refresh the token and call the Retrofit method implementation in switchMap()
If the retrofit call fails because of 401, you can use resultObservable.retryWhen(errorCheck) to check for this error, refresh the access token and call the Retrofit method implementation again.
(I also stored the result in a BehaviorSubject and returned this subject whenever a part of my app requested for the result (by calling an interface method), and I only refreshed the result from network when a separate refresh() call was issued)
Related
I have a Request object with field request_type and number of other fields. request_type can be 'additionRequest' , 'deletionRequest' 'informationRequest'.
Based on request_type other fields in Request object are processed differently.
My simple minded approach is
if additionRequest
algorithm1
else if deletionRequest
algorithm2
else if deletionRequest
algorithm3
end
How I can avoid these if statements and still apply proper algorithm?
If you want to avoid conditional statements then you can leverage object oriented features such as:
Map<String, Function<Request, Result>> parsers = new HashMap<>();
parsers.put("additionRequest", request -> {
// parse and generate Result here
return result;
});
Result result = parsers.get(request.request_type).apply(request);
Seems to me that perhaps a Command Pattern could come in handy here. If you make an object structure of these commands and encapsulate the algorithm that you want to execute within the object, then you can construct the specific sub-objects and later on use "execute" method of the command to invoke the algorithm. Just make sure you are using polymorphic references:
if additionRequest
algorithm1
else if deletionRequest
algorithm2
else if deletionRequest
algorithm3
end
will become
void theRequestExecutor(Reqest req) {
req.executeAlgorithm()
}
https://en.wikipedia.org/wiki/Command_pattern
Use HashMap<RequestType, RequestHandler> for this case. RequestHandler can be an interface which will be implemented for each situation you want to handle.
Hope this help.
You can create Map of key:String, value :Function of RequestType ReturnType. Depending on type of request it will call corresponding function.
Example:
Map<String, Function<RequestType, ResponseType> requestProcessors = new HashMap<>;
requestProcessors.add("additionRequest", this::methodToHandleAddRequest);
requestProcessors.add("deletionRequest", this::methodToHandleDeleteRequest);
Inside request handler do
return this.requestProcessors.get(request.request_type).apply(request);
Note you may have to create response interface if different responses are different. Different response type will inherit from response interface
The object-oriented solution is always to include the logic with the data. In this case include whatever you want a request to do in the request object itself, instead of (presumably) passing out pure data.
Something like
public interface Request {
Response apply();
}
Anything else, like creating a map of functions, creating a thin abstraction layer or applying some pattern is a workaround. These are suitable only if the first solution can not be applied. This might be the case if the Request objects are not under your control, for example generated automatically or third party classes.
I am sending a command and expecting a result via CommandGateway.sendAndWait(Object command, long timeout, TimeUnit unit) method :
Greeting greeting = commandGateway.sendAndWait(new GenericCommandMessage<GetGreetingCommand>(cmd), 1000, TimeUnit.MILLISECONDS);
There are 3 possible outcomes of the above call:
1) Return a non null object.
2) Return a null object (via business logic from my #CommandHandler that queries a DB and doesn't find what I was looking for)
3) Return a null object (returned by Axon framework in case timeout is reached)
I need to implement a way to differentiate between points 2) and 3) so that I can return NOT_FOUND or REQUEST_TIMEOUT statuses accordingly. Do you have any recommendations on how to model this ?
One way that I though of was to add a status field in Greeting (my model object) and in case the #CommandHandler receives nothing from DB I would return a dummy Greeting object with status = -1 (meaning NOT_FOUND), but this solution would mean adding flags to model objects only to differentiate between framework flows and I don't think this is recommended.
The default CommandGateway doesn't allow for this distinction. However, it is possible to define your own gateway, by providing an interface to the GatewayProxyFactory.createGateway() method. Create an instance by passing the CommandBus that the gateway should send messages through in the constructor.
This mechanism allows you to define the behavior that you want, for each method. If you declare a TimeoutException, that exception will be thrown instead of a null return value being returned. If the timeout is the same for each invocation, you can event replace the timeout value parameters (int/long and timeunit) with an annotation.
Check out the javadoc on the GatewayProxyFactory for more details.
hopefully this one is the last question I'm asking on spring integration.
Faced following problem: at the end of pretty long IntegrationFlow dsl sheet there is a code:
return IntegrationFlows.
//...
.enrichHeaders(headerEnricherSpec -> headerEnricherSpec.header("jms_replyTo", responseQueue(), true)) // IntegrationMessageHeaderAccessor.CORRELATION_ID is not acceptable though message came from outgoingGateway of another application with this header been set
.handle(requestRepository::save)
.handle(
Jms.outboundAdapter(queueConnectionFactory()).destination(serverQueue())
)
.get();
The problem is that after some code like requestRepository::save handlers chain becomes broken. This trick only works if there is a gateway passed in as a handler parameter.
How can I overcome this limitation? I think that utilizing wireTap here will not make a deal because it is asynchromous. Here, actually, I save message to store it's jms_replyTo header and replace it with saved one after corresponding reply comes back from server (Smart Proxy enterprise integration pattern).
Any suggestions please?
Not sure why do you say "the last question". Are you going to give up on Spring Integration? :-(
I guess your problem there with the next .handle() because your requestRepository::save is one-way MessageHandler (the void return from the save() method). Or your save() returns null.
The IntegrationFlow is a chain of executions when the next one will be called after the previous with its non-null result.
So, share your requestRepository::save, please!
UPDATE
Neither did help declaring MessageHandler bean as a (m) -> requestRepository.save(m) and passing it into handle(..) method as a param.
Yeah... I would like to see the signature for your requestRepository::save.
So, look. Using method reference for the .handle() you should determine your scenario. If you do one-way handle with the flow stop, there is enough to follow with the org.springframework.messaging.MessageHandler contract. with that you method signature should be like this:
public void myHandle(Message<?> message)
If you'd like to go ahead with the flow you should return anything from your service method. And that result becomes the payload for the next .handle().
In this case your method should follow with the org.springframework.integration.dsl.support.GenericHandler contract. Your method signature may look like:
public Bar myHandle(Foo foo, Map<String, Object> headers)
That's how the method reference works for the .handle().
You should understand how this method chain style works. The output of the previous method is an input of the next. In this our case we protect the flow from the dead code, like MessageHandler with void return, but there is the next flow member. That's why you see This is the end of the integration flow. error.
Finally came up with solution:
#Bean
public MessageHandler requestPersistingHandler() {
return new ServiceActivatingHandler(message -> {
requestRepository.save(message);
return message.getPayload();
});
}
//...
#Bean
public IntegrationFlow requestFlow() {
return IntegrationFlows.from(
Jms.messageDrivenChannelAdapter(queueConnectionFactory()).destination(bookingQueue())
)
.wireTap(controlBusMessageChannel())
.enrichHeaders(headerEnricherSpec -> headerEnricherSpec.header(JMS_REPLY_HEADER, responseQueue(), true))
.handle(requestPersistingHandler())
.handle(
Jms.outboundAdapter(queueConnectionFactory()).destination(serverQueue())
)
.get();
}
I'm just not sure if there is some more straight way.
The only issue left is how to change header in "from-the-server" IntegrationFlow within enrichHeaders method: have no idea how to access existing headers with specification.
Can you please share me the standard method, if any to prevent one method call before the client call another. For example: I have the following two methods in my controller
#RequestMapping(value = “/save”, method = RequestMethod.PUT)
#ResponseBody
public String save(string temp){
return service.update(temp);
}
#RequestMapping(value = “/save/confirm”, method = RequestMethod.PUT)
#ResponseBody
public String ValidateInfo(string temp){
return service.update(temp);
}
So the question is what is the standard way to prevent the client side from calling the save method before calling the validateInfo method using REST API features. Assuming both methods do serious of stuff in the server side.
The client, which might be a different piece of software that you don't control, can make any call at any time. So you can't really prevent a client from making calls. This suggests that your design is a bit flawed.
I would suggest validating the user input as part of the save method - this is usually more secure and robust. If the validation fails, you should consider how you want to notify the client. For example, you could return an error message.
You can return a token from method1 which needs to supplied as parameter to method2..In this way you can make sure that method2 is always called after method1
I have a model which I want to populate with details from a web service. I'd like to do this asynchronously so as not to block a server thread. Let's pretend it's a login service.
Now what I want to do is fire a request to the remote server and ultimately return a User model object. So the method signature would look something like this:
public static User loginUser(String username, String password) {
I understand that to make an asynchronous call to a web service I should use a Promise:
Promise<WS.Response> wsPromise = WS.url("http://myserver.com/login")
.setContentType("application/json; charset=utf-8")
.post("... the username and password ...");
Which doesn't start the request yet. I could call get() on this object to make a blocking call to the service. This works.
To do this asynchronously, I thought I'd need to map this and somehow execute it.
Promise<User> resultPromise = wsPromise.map(new F.Function<WS.Response, User>() {
#Override
public User apply(WS.Response response) throws Throwable {
System.out.println(response.getBody());
return new User(... based on something extracted from the returned JSON ...);
}
});
Now how do I trigger this operation? If I call get() on the resultPromise, it makes the call but eventually fires a Timeout Exception. I can't use the async(...) method because that only returns me a Result.
Looking at other examples (https://github.com/jroper/play-promise-presentation/blob/master/src/main/java/controllers/Application.java), this seems to be the pattern. i.e. We always want to return a Result object. However, I can't query a Result object and I have no plan to send that specific object out to the user.
In fact, those examples seem to call a web service, map the JSON result to an object and then immediately map them back to the same JSON. Not much use when I want to pass the User (in my case) back to the calling function.
To be honest, I'm a little confused with the asynchronous nature of this anyway (you probably guessed that). In particular, this is really a blocking action as we have to wait for the web service to return a response. The documentation seems to indicate that using the Promise / Future patterns will avoid this blocking.
Bottom line is: How do I map the result of a web service call back to a model object without blocking a thread in the Play Framework server?
Feel free to abuse my lack of experience of the Play Framework...
This answer might come way too late, but will post it in case someone else nowadays still wonder the same and want an answer to the question.
There are two ways to achieve this, even though both make use of the same JsonNode class:
The first one is what you were expecting: "new User( ..something...)"
In this case you can make use of JsonNode's "get" method to obtain the specific data you need to create the User object (username and email in my example since you didn't specify the required fields).
Promise<User> resultPromise = wsPromise.map(new F.Function<WS.Response, User>() {
#Override
public User apply(WS.Response response) throws Throwable {
System.out.println(response.getBody());
JsonNode json = response.asJson();
return new User(json.get("username"), json.get("email"));
}
});
The other option is in case you know the Web Service does return a valid Json representation of an User object.
Personally I think this option is way easier and relys on fromJson's method from Json class.
Promise<User> resultPromise = wsPromise.map(new F.Function<WS.Response, User>() {
#Override
public User apply(WS.Response response) throws Throwable {
System.out.println(response.getBody());
return Json.fromJson(response.asJson(), User.class);
}
});
I hope this answer can help people wondering how to do this in a easy way.
.get() or .post(...) on a WS call actually does trigger the WS call to go out. (Don't confuse WS.get() with myPromise.get()... they are quite different).
The trick to keeping this sort of design fully async is to have Promises all the way down, from the Controller response, all the way down.
To do this, you must make judicious use of the map(), flatMat(), and sequence() methods in the Promise API.
In your particular case, you use the result of the WS call inside the code that does the map() that happens when the WS call returns. That is where you use the response to actually do things.
This paradigm is easier in Scala with "for comprehensions", but it's still possible in Java with chains of nested flatMap calls ended with a map call.