Handle Network Thread exceptions in Codename One - codenameone

My app tries to do an async network request at app launch, using RequestBuilder request = Rest.get, as described in this article: https://www.codenameone.com/blog/rest-api-error-handling.html. If I power off my testing server, at the app launch I have this exception:
[Network Thread] 0:0:0,586 - Exception: java.net.ConnectException - Connection refused
but no info, message or dialog is shown to the user. The following boilerplate code in the init() is not called:
addNetworkErrorListener(err -> {
// prevent the event from propagating
err.consume();
if (err.getError() != null) {
Log.e(err.getError());
}
Log.sendLogAsync();
Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null);
});
If I provide a custom network error handler, with request.onError(networkError);, the issue is the same: my network error handler is not called. I tried to implement it so (it's very similar to the above boilerplate code):
private static final ActionListener<NetworkEvent> networkError = new ActionListener<NetworkEvent>() {
#Override
public void actionPerformed(NetworkEvent err) {
Log.p("NETWORK ERROR connecting to the server");
err.consume();
if (err.getError() != null) {
Log.e(err.getError());
}
Log.sendLogAsync();
DialogUtilities.genericNetworkError();
}
};
I tried using the Simulator with the server offline. Then I tried with Android and iOS app, disconnecting the device from the Internet, but also in this case there is no message to the user. What's wrong?
Note that the error code handlers used with onErrorCodeBytes, onErrorCodeJSON and onErrorCodeString seem to work correctly (they are invoked, for example, if I have a 404 http code).

Did you define onError?
It should be invoked for exceptions and might override the global error handling logic once defined.
I tried this case with wifi turned off and it seems to have worked correctly:
Button test = new Button("Test");
test.addActionListener(e -> {
Rest.get("https://www.codenameone.com/").
onError(ee -> {
if(ee.getError() != null) {
Log.e(ee.getError());
}
}).
fetchAsBytes(call -> {});
});

Related

Distinguish between server-side errors and connection problems

As described in the Codename One blog post "Terse Table, Radar Chart and Networking Enhancements":
Better Error Code Handling
Up until recently if we got an error response code it wasn’t sent
through the global error handler and was handled via the local error
handling chain first. This is no longer the case and these errors are
now handled correctly.
However, if you relied on that misbehavior of older versions we have
setHandleErrorCodesInGlobalErrorHandler(boolean). This defaults to
true, you can set it to false to change the default behavior.
So, the global error handler is invoked both by server-side errors and connection problems. It's a very good thing to be able to manage networks errors in the most generic way, however I need to distinguish between server-side errors and connection problems.
After some testing, I wrote the following code, tested on Simulator, Android and iOS:
addNetworkErrorListener(err -> {
// prevents the event from propagating
err.consume();
if (err.getError() != null) {
// this is the case of a network error,
// like: java.io.IOException: Unreachable
Log.p("Error connectiong to: " + err.getConnectionRequest().getUrl());
// maybe there are connectivity issues, let's try again
ToastBar.showInfoMessage("Trying to reconnect...");
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
err.getConnectionRequest().retry();
}
}, 2000);
} else {
// this is the case of a server error
// logs the error
Log.p("REST ERROR\nURL:" + err.getConnectionRequest().getUrl()
+ "\nMethod: " + err.getConnectionRequest().getHttpMethod()
+ "\nRequest body: " + err.getConnectionRequest().getRequestBody()
+ "\nResponse code: " + err.getConnectionRequest().getResponseCode()
+ "\nResponse message: " + StringUtilities.toString(err.getConnectionRequest().getResponseData()),
Log.ERROR);
Log.sendLogAsync();
ToastBar.showErrorMessage("Server Error", 10000);
}
});
In the tested platforms, server-side errors are reported, but no further action follows; connectivity errors are both reported and managed by retrying the connection every two seconds.
So, that code works.
Anyway, what I don't like is the assumption that err.getError() != null is true for connectivity errors, false for server-side errors. I came to this assumption after several tests, but I didn't find it documented anywhere.
So my question is whether:
my assumption is always correct;
there is a better way to distinguish server-side errors from connectivity errors.
Thank you

Handling 307 and 301 in ConnectionRequest

I am using ConnectionRequest and downloadImageToStorage to download an image from a web server. The webserver is returning a 307 with a location, that next url returns a 301 with another location, that location finally returns the actual image.
I defined a FailureCallback, but it is not getting called at all. All I can see is a message on the device screen that indicates the error and has a retry/cancel buttons.
Here is my code
FailureCallback<Image> failure = new FailureCallback<Image>() {
public void onError(Object sender, Throwable err, int errorCode, String errorMessage) {
System.out.println(errorCode);
if (errorCode == 307 || errorCode == 301) {
System.out.println(sender.getClass().getName());
}
}
};
ConnectionRequest request = new ConnectionRequest();
request.setUrl(url);
request.setHttpMethod("GET");
request.downloadImageToStorage("img_" + imgCount, (img) -> {
SpanLabel t = new SpanLabel();
t.add(BorderLayout.CENTER, img);
myform.add(t);
}, failure);
++imgCount;
How can I capture these errors so I can get the Location from the Response objects and call those with new ConnectionRequests?
Note that I can see the chain of error codes and locations when I use the debugger in Chrome to get the image that I am trying to load.
Thanks
To capture error code and error message setReadResponseForErrors method should be called and override handleErrorResponseCode method of ConnectionRequest as shown in below code
request .setReadResponseForErrors(true);
#Override
protected void handleErrorResponseCode(int code, String message) {
Dialog.show("Message", " code "+ code+" msg "+message, "ok", null);
}
As Shai mentioned in his comment, there was a bug that caused 307 to be interpreted as an error response, rather than a redirect. This should now be fixed in the latest, and this should cause your example to work.
There is also another bug which has not been fixed that prevents the onError() callback from being called when there is an error response code. This will be fixed soon. In the mean time, your example should work (and the onError() will not be called) with 301 and 307 response codes.

codenameone: how to handle exception java.net.ConnectionException explicitly

codenameone: how to handle exception java.net.ConnectionException explicitly
I want to handle exception explicitly.Currently when I am handling exception It handled implicitly first in which shows the exception message on screen in detail.I don't want to show in detail error message on screen(pop up dialog).
right now it shows the exception Java.net.Connection Exception: Connection refused for URL http:localhost/login connection refused.instead of this message i just want to show "connection refused" message on pop-up dialog
Can you please let me know how to resolve it.
On mobile devices the error might be quite different to the one on the simulator since we are dealing with native API's under the surface. See the error handling section of the networking section in the developer guide:
There are two distinct placed where you can handle a networking error:
The ConnectionRequest - by overriding callback methods
The NetworkManager error handler
Notice that the NetworkManager error handler takes precedence thus allowing you to define a global policy for network error handling by consuming errors.
E.g. if I would like to block all network errors from showing anything to the user I could do something like this:
NetworkManager.getInstance().addToQueue(request);
NetworkManager.getInstance().addErrorListener((e) -> e.consume());
The error listener is invoked first with the NetworkEvent matching the error. Consuming the event prevents it from propagating further down the chain into the ConnectionRequest callbacks.
We can also override the error callbacks of the various types in the request e.g. in the case of a server error code we can do:
ConnectionRequest request = new ConnectionRequest(url, false) {
protected void handleErrorResponseCode(int code, String message) {
if(code == 444) {
// do something
}
}
protected void handleException(Exception err) {
// handle exception that occurred. Notice you can either have this or have the listener on the NetworkManager
}
protected void readResponse(InputStream input) {
// just read from the response input stream
}
};
NetworkManager.getInstance().addToQueue(request);

Only one usage of each socket address (protocol/network address/port) is normally permitted

The last few weeks we have been experiencing this error message while using the Azure Search SDK (1.1.1 - 1.1.2) and performing searches.
We consume the Search SDK from internal APIs (deployed as Azure Web Apps) that scale up-down based on traffic (so there could be more than 1 instance of the APIs doing the searches).
Our API queries 5 different indexes and maintains an in-memory copy of the SearchIndexClient object that corresponds to each index, a very simple implementation would look like:
public class AzureSearchService
{
private readonly SearchServiceClient _serviceClient;
private Dictionary<string, SearchIndexClient> _clientDictionary;
public AzureSearchService()
{
_serviceClient = new SearchServiceClient("myservicename", new SearchCredentials("myservicekey"));
_clientDictionary = new Dictionary<string, SearchIndexClient>();
}
public SearchIndexClient GetClient(string indexName)
{
try
{
if (!_clientDictionary.ContainsKey(indexName))
{
_clientDictionary.Add(indexName, _serviceClient.Indexes.GetClient(indexName));
}
return _clientDictionary[indexName];
}
catch
{
return null;
}
}
public async Task<SearchResults> SearchIndex(SearchIndexClient client, string text)
{
var parameters = new SearchParameters();
parameters.Top = 10;
parameters.IncludeTotalResultCount = true;
var response = await client.Documents.SearchWithHttpMessagesAsync(text, parameters, null, null);
return response.Body;
}
}
And the API would invoke the service by:
public class SearchController : ApiController
{
private readonly AzureSearchService service;
public SearchController()
{
service = new AzureSearchService();
}
public async Task<HttpResponseMessage> Post(string indexName, [FromBody] string text)
{
var indexClient = service.GetClient(indexName);
var results = await service.SearchIndex(indexClient, text);
return Request.CreateResponse(HttpStatusCode.OK, results, Configuration.Formatters.JsonFormatter);
}
}
We are using SearchWithHttpMessagesAsync due to a requirement to receive custom HTTP headers instead of the SearchAsync method.
This way we avoid opening/closing the client under traffic bursts. Before using this memory cache (and wrapping each client on a using clause) we would get port exhaustion alerts on Azure App Services.
Is this a good pattern? Could we be receiving this error because of the multiple instances running in parallel?
In case it is needed, the stack trace shows:
System.Net.Http.HttpRequestException: Only one usage of each socket address (protocol/network address/port) is normally permitted service.ip.address.hidden:443
[SocketException:Only one usage of each socket address (protocol/network address/port)is normally permitted service.ip.address.hidden:443]
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure,Socket s4,Socket s6,Socket& socket,IPAddress& address,ConnectSocketState state,IAsyncResult asyncResult,Exception& exception)
[WebException:Unable to connect to the remote server]
at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext& context)
at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
EDIT: We are also receiving this error A connection attempt failed because the connected party did not properly respond after a period of time:
System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond service.ip.address.hidden:443
[SocketException:A connection attempt failed because the connected party did not properly respond after a period of time,or established connection failed because connected host has failed to respond service.ip.address.hidden:443]
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure,Socket s4,Socket s6,Socket& socket,IPAddress& address,ConnectSocketState state,IAsyncResult asyncResult,Exception& exception)
[WebException:Unable to connect to the remote server]
at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext& context)
at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
As implemented in the code in your question, the cache will not prevent port exhaustion. This is because you're instantiating it as a field of the ApiController, which is created once per request. If you want to avoid port exhaustion, the cache must be shared across all requests. To make it concurrency-safe, you should use something like ConcurrentDictionary instead of Dictionary.
The "connection attempt failed" error is likely unrelated.

silverlight client doesnt understand faultexception

I have a WCF service that throws an exception which I am trying to catch unsucessfully in my silverlight client code. I am using Undeclared Faults for Debugging purposes and this is my service method :
[OperationContract]
public ServiceResponse MyWCFServiceMethod()
{
ServiceResponse resp = new ServiceResponse ();
//code for setting resp...
//purposely throw error here.
throw new FaultException(new FaultReason(new FaultReasonText("My fault Reason!")),new FaultCode("my fault code here"));
return resp;
}
Now in my silverlight client view model, in the service's callback method, I try to handle it like this:
private void MyServiceCallback(MyWCFServiceMethodCompletedEventArgs e)
{
if (e.Error == null)
{
//proceed normally
}
else if (e.Error is FaultException)
{
FaultException<ExceptionDetail> fault = e.Error as FaultException<ExceptionDetail>;
MessageBox.Show(fault.Detail.Message);
MessageBox.Show(fault.Reason.ToString());
}
}
at this line else if (e.Error is FaultException) I still get System.Net.WebException {The remote server returned an error: NotFound.}
These are the config entries
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
This is the service class declaration
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MySilverlightWCFService
{
....
This service is in another project within the same silverlight solution.
Why is my silverlight client not able to get the fault exception I am throwing?
Thanks for your time...
ok so finally what seems to be the way to make this work is to get one line of code added to the service just before you throw the fault exception!
System.ServiceModel.Web.WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
Then throw the actual exception:
throw new FaultException(new FaultReason(new FaultReasonText("My fault Reason!")),new FaultCode("my fault code here"));
Then in silverlight modify the service call back error handling section from what I put in my question above to:
//else if (e.Error is FaultException)
else
{
//FaultException<ExceptionDetail> fault = e.Error as FaultException<ExceptionDetail>;
//MessageBox.Show(fault.Detail.Message);
FaultException fault = e.Error as FaultException;
MessageBox.Show(fault.Reason.ToString());
}
That worked for me. Ugly way!
I will try with Declared faults when I get the time.
Check out the SilverlightFaultBehavior. It will handle changing the status code for you.
The server is probably throwing a HTTP 500 response code that Silverlight is ignoring. You must change the service to return a HTTP code that Silverlight will accept.
From Data Performance and Fault Strategies in Silverlight 3: (This article will show you how to return WCF faults to Silverlight.)
Infamous NotFound Error:
When the exception is raised, an HTTP
status
code of 500 is returned to
Silverlight. The browser networking
stack prevents Silverlight from
reading responses with a status code
of 500, so any SOAP fault information
contained within is unavailable to the
Silverlight client application. Even
if the message could be retrieved,
Silverlight 2 is not capable of
converting the fault back into a
managed exception. Both of these
issues have been addressed in
Silverlight 3.

Resources