Repeat a Retrofit2.0 api-call based on the server response - retrofit

Using Retrofit2 (Ver: 2.3.0), OkHTTP3 (Ver: 3.9.0) with RxJava2 (Ver: 2.0.0)
Scenario of a rest api call
Observable<ServerResponse> backendService.isUserVerified("someuserName")
which returns a 200 Ok HTTP
Expected Success Response JSON POJO if user is verified
{
user: "someUserName",
isVerified: "yes"
}
Expected Success Response JSON POJO if user is not verified
{
user: "someUserName",
isVerified: "no"
}
Following is code I am trying to implement
backendService.isUserVerified("someuserName")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
//Want to add a flatmap (or some other operator)
.flatmap(new Function<ServerResponse>, ObservableSource<?>>(){
#Override
public ObservableSource<?> apply(ServerResponse serverResponse) throws Exception {
if(0 != serverResponse.getisVerified.compareToIgnoreCase("yes")) {
return Observable.error(new CustomException());
}
return //current observable.
}
.retryWhen(mRetryHandler)
.subscribeWith(observer);
The intent is to throw a custom exception if verified == no so that retryWhen() operator can pitch in to repeat the call chain, else want to proceed through the chain till subscribe().
Any pointers / help ?

You can just use doOnNext rather than flatmap:
backendService.isUserVerified("someuserName")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
//we can throw an exception if the user is not verified or keep the response as it is otherwise
.doOnNext(serverResponse -> {
if(0 != serverResponse.getisVerified.compareToIgnoreCase("yes"))
{
throw new CustomException();
}
})
.retryWhen(mRetryHandler)
.subscribeWith(observer);
The doOnNext operator just performs a function with the emitted item without changing it. You just want to throw an error in a specific case based on the item and otherwise not to change it so it's an appropriate operator to use. As you don't change threads before the doOnNext operator or after it, before using the item in the next operator you know it will throw the exception BEFORE continuing in case it should throw it.
Here's an example from the linked documentation of the operator:
Observable.just(1, 2, 3)
.doOnNext(new Action1<Integer>() {
#Override
public void call(Integer item) {
if( item > 1 ) {
throw new RuntimeException( "Item exceeds maximum value" );
}
}
}).subscribe(new Subscriber<Integer>() {
#Override
public void onNext(Integer item) {
System.out.println("Next: " + item);
}
#Override
public void onError(Throwable error) {
System.err.println("Error: " + error.getMessage());
}
#Override
public void onCompleted() {
System.out.println("Sequence complete.");
}
});
[output:]
Next: 1
Error: Item exceeds maximum value

If you want to throw an error using Observable.error(new CustomException());, you don't need to make function to return a specific object for the error and another one for the expected answer, it's an exception and will go to onError() method. Your ObservableResource<?> must return your expected answer that is retrieved in onResponse() method from Subscriber. If you throw an error using Observable.error(), your chain will be broken and error will be go to onError().
Edit
You should use retry function, because will return you ServerResponse and you can check if is valid or not. Using retryWhen, you get only the error when something goes wrong. In your case, you don't get any error, you get the answer. In this case, you don't need that flatMap
.retry(new BiPredicate<ServerResponse, Throwable>() {
#Override
public boolean test(ServerResponse serverResponse, Throwable throwable) throws Exception {
return serverResponse.getisVerified.equals("no");
}
})

Related

Flutter and external JAR Library: android.os.NetworkOnMainThreadException

Im trying to use olingo with Flutter on Android. I set up my channel and I can call the library but I keep getting this message:
E/AndroidRuntime(28391): FATAL EXCEPTION: main
E/AndroidRuntime(28391): Process: com.example.odata, PID: 28391
E/AndroidRuntime(28391): org.apache.olingo.client.api.http.HttpClientException: android.os.NetworkOnMainThreadException
E/AndroidRuntime(28391): at org.apache.olingo.client.core.communication.request.AbstractODataRequest.doExecute(AbstractODataRequest.java:312)
So it looks like it is running on the main thread - which is a no go as this would block. I tried the looper to ask Java to run on the UI Thread:
public void onMethodCall(MethodCall call, Result result) {
// Note: this method is invoked on the main thread.
Log.i("test", "using " + call.method);
String serviceUrl = "http://services.odata.org/OData/OData.svc/";
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
if (call.method.equals("getMetaData")) {
String metadata;
final Edm edm = ODataClientFactory.getClient().getRetrieveRequestFactory().getMetadataRequest(serviceUrl).execute().getBody();
metadata = edm.toString();
if (metadata != "") {
result.success(metadata);
} else {
result.error("UNAVAILABLE", "Metadata cannot read.", null);
}
} else {
result.notImplemented();
}
}
});
But Im still getting the same error.
So how exactly can I deal with external JAR Library which are doing blocking operations ? To my understanding an external call is a Future anyway so it will not block my Flutter thread anyway - but Android Java does not think so ...
This is my method call in flutter
Future<void> _getMetaData() async {
String metadata;
try {
final String result = await platform.invokeMethod('getMetaData');
metadata = result;
} on PlatformException catch (e) {
metadata = e.message;
}
setState(() {
_metadata = metadata;
});
}
Thanks for the answer, this is the solution for anyone that may be interested:
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getMetaData")) {
class MetadataLoader extends AsyncTask<String , Integer, String> {
#Override
protected String doInBackground(String... urls) {
// call your Java library method here, including blocking methods
return your_return_value;
}
protected void onPostExecute(String _result) {
// your_return_value is now passed in _result
result.success(_result);
}
}
new MetadataLoader().execute(); // Start the Async
}
On the flutter side,
Future<void> _getMetaData() async {
String metadata;
try {
final String result = await platform.invokeMethod('getMetaData');
// do something with the result
// the Flutter thread will stop at the await and resume when the Java
// will call result.success
}
}
You will need to create a new Java thread or Worker. (Note that the "main" thread and the "UI" thread are the same thing - so by posting to the main looper you've ended up in the same place - trying to do network i/o on the main thread.)
Yes, the Flutter engine is running in different threads, but you still need to leave the main native thread unblocked as it is responsible for detecting user input, etc.
Also note that when your blocking activity completes - on its non-main thread - it will likely want to deliver the response to Dart. To do this it will need to use part of your code above - to post the results back to the main thread, which can then invoke method channel operations.
You'll probably want to use your method channel bi-directionally. From flutter to native to request an operation (returning, say, a sequence number), and from native to flutter to deliver the results (quoting the sequence number so that the result can be tied back to the request).

How to know FirebaseRecyclerAdapter query is zero or not, exist or not

How to know FirebaseRecyclerAdapter query is zero or not, exist or not
I find some instructions on
https://github.com/firebase/FirebaseUI-Android/tree/master/database
it says:
Data and error events
When using the FirebaseRecyclerAdapter you may
want to perform some action every time data changes or when there is
an error. To do this, override the onDataChanged() and onError()
methods of the adapter:
FirebaseRecyclerAdapter adapter = new FirebaseRecyclerAdapter<Chat, ChatHolder>(options) {
// ...
#Override
public void onDataChanged() {
// Called each time there is a new data snapshot. You may want to use this method
// to hide a loading spinner or check for the "no documents" state and update your UI.
// ...
}
#Override
public void onError(DatabaseError e) {
// Called when there is an error getting data. You may want to update
// your UI to display an error message to the user.
// ...
}
};
When I tried to use as follow:
mAdapter = new FirebaseRecyclerAdapter<Place, PlaceViewHolder>(options)
{
#Override
public void onDataChanged(DataSnapshot dataSnapshot)
{
// Called each time there is a new data snapshot. You may want to use this method
// to hide a loading spinner or check for the "no documents" state and update your UI.
// ...
if (dataSnapshot.exists())
{
Log.d(TAG,"data exists");
}
else
{
Log.d(TAG,"No data exists");
}
}
#NonNull
#Override
public PlaceViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i)
{
Error message is:
Method does not override method from its superclass
So how can I solve this problem, Thank you.
I found the answer from another page:
https://www.programcreek.com/java-api-examples/?api=com.firebase.ui.database.FirebaseRecyclerAdapter
#Override
public void onDataChanged()
{
// Called each time there is a new data snapshot. You may want to use this method
// to hide a loading spinner or check for the "no documents" state and update your UI.
// ...
if (getItemCount() == 0)
{
Log.d(TAG,"No data exists");
}
else
{
Log.d(TAG,"data exists");
}
}

JavaFX Why The Task returns Null?

I'm trying to learn Threads and Tasks for days now ... but still can't implement it in my app ... help plz.
I want to run all Database operations in a background thread other than the application thread.
I have a class that manages the Database queries ... in this class i surrounded the executeQuery statement with a task:
public class Database {
ResultSet rs;
public ResultSet execQuery(PreparedStatement stmnt) throws SQLException {
Task<ResultSet> task = new Task<ResultSet>() {
#Override protected ResultSet call() throws Exception {
if (isCancelled()) {
}
ResultSet execRs = stmnt.executeQuery();
return execRs;
}
};
task.run();
task.setOnSucceeded(new EventHandler<WorkerStateEvent>(){
#Override
public void handle(WorkerStateEvent event) {
rs = task.getValue();
}
});
return rs;
}
// remaining Code
}
As you can see the method should return a ResultSet but when i call this from another place it raise a null pointer ... the result set returned by this method is null.
So what did i do wrong here?
Update #1
#James_D Thank you for this great link ... i think i finally understand the concept ... but still have a small problem with implementing it ... for example in my authentication method after the user is authenticated i want to check if that user has an open shift ... so following your link i changed the method to this:
private boolean isShiftOpen(int userId, int branchId, int comId) throws SQLException, ClassNotFoundException {
// final boolean success = false;
Task<Shift> task = new Task<Shift>(){
#Override
protected Shift call() throws Exception {
return ShiftDAO.getShift(userId, branchId, comId);
}
};
task.setOnFailed(e -> {
System.out.println("isShiftOpenTask Faild!!");
success = false;
});
task.setOnSucceeded(e -> {
System.out.println("isShiftOpenTask Succeeded!!");
Shift shift1 = task.getValue();
System.out.println("User Open Shift Exists ... returning true");
SessionBean.setShiftId(shift1.getShiftId());
SessionBean.setUserId(shift1.getUserId());
SessionBean.setUserBranch(branchId);
success = true;
});
exec.execute(task);
return success;
}
I have two problems:
1- The exec.execute(task) raise a nullpoint exception.
2- I wanted to use a boolean variable returned by this method ... the only way i could access such a variable is to define it outside the method ... but then if i want to use another method like this one i must declare another boolean variable for it to ... does this sound right to you?
Thank you for your time
Gado
You call
task.run();
which executes the task on the current thread, i.e. that statement will not complete until the task finishes. Then you call
task.setOnSucceeded(...);
which essentially says "when the task succeeds, set the instance variable rs to the result of the task. However, by the time you call this, the task has already succeeded (or possibly failed), so there is no way for the handler to be invoked.
You could fix the null result by reversing the order of these calls, i.e. do
public ResultSet execQuery(PreparedStatement stmnt) throws SQLException {
Task<ResultSet> task = new Task<ResultSet>() {
#Override protected ResultSet call() throws Exception {
if (isCancelled()) {
}
ResultSet execRs = stmnt.executeQuery();
return execRs;
}
};
task.setOnSucceeded(new EventHandler<WorkerStateEvent>(){
#Override
public void handle(WorkerStateEvent event) {
rs = task.getValue();
}
});
task.run();
return rs;
}
However, since you are executing the task on the current thread, it's not really clear what the point of using a task at all is: you may as well just execute the database query directly in your execQuery method and return the result directly. In other words, the above code is equivalent to
public ResultSet execQuery(PreparedStatement stmnt) throws SQLException {
rs = stmnt.executeQuery();
return rs;
}

WF 4 OnUnhandledException not hit

I've created a custom activity which contains as a Body another Activity.
[Browsable(false)]
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete), OnFaulted);
}
private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
{
throw new Exception(propagatedException.Message);
}
When an exception is thrown during the execution of the Body, ma handler for the OnFaulted is hit.
My execution starts with a call to static method Run of the WorkflowApplication class. My WorkflowApplication instance has a handler associated for the OnUnhandledException event.
instance.OnUnhandledException +=
delegate(WorkflowApplicationUnhandledExceptionEventArgs args)
{
Console.WriteLine(args.ExceptionSource);
waitEvent.Set();
return UnhandledExceptionAction.Cancel;
};
But regardless of what happens when the Activity hosted in the Body is executed, i never reach the handler defined above. I thought that if i throw an exception from the OnFaulted, i will be able to redirect the flow to the OnUnhandledException but i was wrong. Any ideas ?
I need this in order to centralize my errors, check them and display messages accordingly. Also i need a way to stop the execution and so on and i don't want to define handlers all over the application. Is there any way to accomplish this ?
As Will suggested, i will post what i did to handle my scenario.
Basically, in my custom activity i have hosted an Assign :
[Browsable(false)]
public Activity Body { get; set; }
Activity System.Activities.Presentation.IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
return new Assignment()
{
Body = new Assign() { DisplayName = "" }
};
}
I've added this code to my Execute method :
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete), OnFaulted);
I was trying to run this Assignment by giving an array a negative value as index and and an exception was thrown. This, somehow ended my execution but no handler for the events of my WorkflowApplication instance were hit.
Here is the method given as a callback when executing the body ( in our case the Assign activity ) :
private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
{
faultContext.HandleFault();
CommunicationExtension ce = faultContext.GetExtension<CommunicationExtension>();
ITextExpression toTextExpression = (propagatedFrom.Activity as Assign).To.Expression as ITextExpression;
string valueTextExpression = string.Empty;
if ((propagatedFrom.Activity as Assign).Value != null)
{
if ((propagatedFrom.Activity as Assign).Value.Expression != null)
valueTextExpression = (propagatedFrom.Activity as Assign).Value.Expression.ToString();
}
if (ce != null)
{
ce.AddData(string.Format("{0} found on Assignment definition [{1} = {2}]", propagatedException.Message, toTextExpression.ExpressionText, valueTextExpression));
}
}
The trick was to call :
faultContext.HandleFault();
and use CommunicationExtension to allow me to to display the erros in the GUI.
The code for this class is trivial :
public class CommunicationExtension
{
public List<string> Messages { get; set; }
public CommunicationExtension()
{
Messages = new List<string>();
}
public void AddData(string message)
{
if (string.IsNullOrEmpty(message))
return;
Messages.Add(message);
}
}
Use this to add the extension:
CommunicationExtension ce = new CommunicationExtension();
instance.Extensions.Add(ce);
where instance is my WorkflowApplication instance.
I understood that for each instance of the workflow application we have one instance of its extension class. So i can send messages like this from all my custom activities in order to display their status.
I hope this scenario can help other people too.

Spring MVC Validation - Avoiding POST-back

I'd like to validate a Spring 3 MVC form. When an element is invalid, I want to re-display the form with a validation message. This is pretty simple so far. The rub is, when the user hits refresh after an invalid submission, I don't want them to POST, I want them to GET. This means I need to do a redirect from the form POST (submission) to re-display the form with validation messages (the form is submitted via a POST).
I'm thinking the best way to do this is to use SessionAttributeStore.retrieveAttribute to test if the form is already in the user's session. If it is, use the store form, otherwise create a new form.
Does this sound right? Is there a better way to do this?
To solve this problem, I store the Errors object in the session after a redirect on a POST. Then, on a GET, I put it back in the model. There are some holes here, but it should work 99.999% of the time.
public class ErrorsRedirectInterceptor extends HandlerInterceptorAdapter {
private final static Logger log = Logger.getLogger(ErrorsRedirectInterceptor.class);
private final static String ERRORS_MAP_KEY = ErrorsRedirectInterceptor.class.getName()
+ "-errorsMapKey";
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView mav)
throws Exception
{
if (mav == null) { return; }
if (request.getMethod().equalsIgnoreCase(HttpMethod.POST.toString())) {
// POST
if (log.isDebugEnabled()) { log.debug("Processing POST request"); }
if (SpringUtils.isRedirect(mav)) {
Map<String, Errors> sessionErrorsMap = new HashMap<String, Errors>();
// If there are any Errors in the model, store them in the session
for (Map.Entry<String, Object> entry : mav.getModel().entrySet()) {
Object obj = entry.getValue();
if (obj instanceof Errors) {
if (log.isDebugEnabled()) { log.debug("Adding errors to session errors map"); }
Errors errors = (Errors) obj;
sessionErrorsMap.put(entry.getKey(), errors);
}
}
if (!sessionErrorsMap.isEmpty()) {
request.getSession().setAttribute(ERRORS_MAP_KEY, sessionErrorsMap);
}
}
} else if (request.getMethod().equalsIgnoreCase(HttpMethod.GET.toString())) {
// GET
if (log.isDebugEnabled()) { log.debug("Processing GET request"); }
Map<String, Errors> sessionErrorsMap =
(Map<String, Errors>) request.getSession().getAttribute(ERRORS_MAP_KEY);
if (sessionErrorsMap != null) {
if (log.isDebugEnabled()) { log.debug("Adding all session errors to model"); }
mav.addAllObjects(sessionErrorsMap);
request.getSession().removeAttribute(ERRORS_MAP_KEY);
}
}
}
}
It's not clear from your question but it sounds like your GET and POST actions are mapped to the same handler. In that case you can do something like:
if ("POST".equalsIgnoreCase(request.getMethod())) {
// validate form
model.addAttribute(form);
return "redirect:/me.html";
}
model.addAttribute(new MyForm());
return "/me.html";
In the JSP check if there are any error on the form and display as needed.
Such approach is called PRG (POST/REdirect/GET) design pattern I explained it few days ago as one of the answers:
Spring MVC Simple Redirect Controller Example
Hope it helps :)

Resources