How do I get Codenameone android App to reliably connect to REST API? - codenameone

I have built a codename one android app that connects a restdb.io database backend using REST. However, I am having random network failures like below
> [EDT] 0:4:26,949 - Exception: java.net.ConnectException - Failed to connect to ziemozi-a3ef.restdb.io/188.166.28.84:443
> java.net.ConnectException: Failed to connect to
> ziemozi-a3ef.restdb.io/188.166.28.84:443 at
> com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:147)
> at
> com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
> at
> com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
> at
> com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
> at
> com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
> at
> com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:302)
> at
> com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:245)
> at
> com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
> at
> com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
> at
> com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:542)
> at
> com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:106)
> at
> com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:30)
> at
> com.codename1.impl.android.AndroidImplementation.getResponseCode(AndroidImplementation.java:6113)
> at
> com.codename1.io.ConnectionRequest.performOperationComplete(ConnectionRequest.java:905)
> at
> com.codename1.io.NetworkManager$NetworkThread.run(NetworkManager.java:341)
> at
> com.codename1.impl.CodenameOneThread$1.run(CodenameOneThread.java:60)
> at java.lang.Thread.run(Thread.java:919)
OR
> java.net.SocketTimeoutException: timeout
at com.android.okhttp.okio.Okio$3.newTimeoutException(Okio.java:214)
at com.android.okhttp.okio.AsyncTimeout.exit(AsyncTimeout.java:263)
at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:217)
at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:307)
at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:301)
at com.android.okhttp.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:197)
at com.android.okhttp.internal.http.Http1xStream.readResponse(Http1xStream.java:188)
at com.android.okhttp.internal.http.Http1xStream.readResponseHeaders(Http1xStream.java:129)
at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:770)
at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:642)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:475)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:542)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:106)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:30)
at com.codename1.impl.android.AndroidImplementation.getResponseCode(AndroidImplementation.java:6113)
at com.codename1.io.ConnectionRequest.performOperationComplete(ConnectionRequest.java:905)
at com.codename1.io.NetworkManager$NetworkThread.run(NetworkManager.java:341)
at com.codename1.impl.CodenameOneThread$1.run(CodenameOneThread.java:60)
at java.lang.Thread.run(Thread.java:919)
Caused by: java.net.SocketException: socket is closed
at com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read(ConscryptFileDescriptorSocket.java:554)
at com.android.okhttp.okio.Okio$2.read(Okio.java:138)
at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:213)
This happens in both simulator and device. And also when using fixed internet or mobile internet though it appears to happen more with mobile internet,

Both of the logs you reported seem to refer to connectivity issues, rather than server-side errors. You can't avoid connectivity issues, in fact, you should assume them to be normal, especially on mobile devices. The question then is how to handle them.
There is no universally valid answer, it depends on what you want to achieve. What many apps do, in these cases, is to report the problem to the user while continuously trying to repeat the failed operation, i.e. in this case the REST request, until an HTTP code is received in response.
For this kind of approach, I had asked a question back in the day, you can read it here: Distinguish between server-side errors and connection problems
In short, I removed the default error handling code from init() and I invoke the following well tested and working method for the purpose (it is a version based on the code in Distinguish between server-side errors and connection problems), that you can customize according to your needs:
private void addNetworkAndServerErrorListener() {
// The following way to manage network errors is discussed here:
// https://stackoverflow.com/questions/61993127/distinguish-between-server-side-errors-and-connection-problems
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(), Log.ERROR);
// maybe there are connectivity issues, let's try again
ToastBar.showInfoMessage("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
String errorLog = "REST ERROR\nURL:" + err.getConnectionRequest().getUrl()
+ "\nMethod: " + err.getConnectionRequest().getHttpMethod()
+ "\nResponse code: " + err.getConnectionRequest().getResponseCode();
if (err.getConnectionRequest().getRequestBody() != null) {
errorLog += "\nRequest body: " + err.getConnectionRequest().getRequestBody();
}
if (err.getMetaData() != null) {
errorLog += "\nMetaData: " + err.getMetaData().toString();
}
if (err.getConnectionRequest().getResponseData() != null) {
errorLog += "\nResponse message: " + new String(err.getConnectionRequest().getResponseData());
}
if (err.getConnectionRequest().getResponseErrorMessage() != null) {
errorLog += "\nResponse error message: " + err.getConnectionRequest().getResponseErrorMessage();
}
if (err.getMessage() != null) {
errorLog += "\nResponse error message: " + err.getMessage();
}
Log.p(errorLog, Log.ERROR);
Log.sendLogAsync();
ToastBar.showErrorMessage("Server Error", 10000);
}
});
}

Related

How to handle keys pressed almost in the same time?

I'm trying to resolve a problem with the search bar. It works but the problem is that if I press two keys almost at the same time, the app will only search the words with the first key pressed.
Here are the logs:
In this one, it works when I press the P then R:
[EDT] 0:4:9,283 - p
[EDT] 0:4:9,348 - 10
[EDT] 0:4:9,660 - pr
[EDT] 0:4:9,722 - 3
The second one doesn't because I press P and R nearly at the same time:
[EDT] 0:4:35,237 - p
[EDT] 0:4:35,269 - pr
[EDT] 0:4:35,347 - 0
[EDT] 0:4:35,347 - 10
The logs here are generated to show the String searched and the result size. As you can see, the first case get results before typing the next char and the second case got all results when the two chars are typed.
The main problem is that in the second case, results from the 'p' String are shown instead of those of 'pr'.
I'm using the searchbar from the Toolbar API with addSearchCommand and an InfiniteContainer to show result data.
Could it be a problem in the order of the events from the addSearchCommand are treated ?
EDIT: Here is the client side code. Server side it's just a simple rest service call which fetch the data from the database.
public static ArrayList<Patient>getSearchedPatient(int index,int amount, String word)
{
ArrayList<Patient> listPatient = null;
Response reponse;
try {
reponse = RestManager.executeRequest(
Rest.get(server + "/patients/search")
.queryParam("index", String.valueOf(index))
.queryParam("amount", String.valueOf(amount))
.queryParam("word", word),
RequestResult.ENTITIES_LIST,
Patient.class);
listPatient = (ArrayList<Patient>)reponse.getResponseData();
Log.p(""+listPatient.size());
} catch (RestManagerException e) {
LogError("", e);
}
return listPatient;
}
private static Response executeRequest(RequestBuilder req, RequestResult type, Class objectClass) throws RestManagerException
{
Response response = null;
try {
switch (type) {
case BYTES:
response = req.getAsBytes();
break;
case JSON_MAP:
response = req.acceptJson().getAsJsonMap();
break;
case ENTITY:
response = req.acceptJson().getAsProperties(objectClass);
break;
case ENTITIES_LIST:
response = req.acceptJson().getAsPropertyList(objectClass);
break;
default:
case STRING:
response = req.getAsString();
break;
}
} catch (Exception e) {
log().error("Erreur à l'exécution de la requête", e);
response = null;
}
if(response == null)
return null;
return response;
}
So the trick here is a simple one. Don't make a request... Most users type fast enough to saturate your network connection speed so you will see completion suggestions referring to things that are no longer relevant.
This is a non-trivial implementation which I discuss in-depth in the Uber book where such a feature is implemented.
The solution is to send a request after a delay while caching responses to avoid double requests and ideally canceling request in progress when applicable. The solution in the Uber book does all 3 I'll try to cover just the basics in this mockup code. First you need a field for the timer and current request. Ideally you would also have a Map containing cached data:
private UITimer delayedRequest;
private String currentSearch;
private Map<String, String> searchCache = new HashMap<>();
Then you need to bind a listener like this:
tb.addSearchCommand(e -> {
String s = (String)e.getSource();
if(s == null) {
if(delayedRequest != null) {
delayedRequest.cancel();
delayedRequest = null;
}
return;
}
if(currentSearch != null && s.equals(currentSearch)) {
return;
}
if(delayedRequest != null) {
delayedRequest.cancel();
delayedRequest = null;
}
currenSearch = s;
delayedRequest = UITimer.timer(100, false, () -> {
doSearchCode();
});
});
I didn't include here usage of the cache which you need to check within the search method and fill up in the result code. I also didn't implement canceling requests already in progress.

Spring LDAP AD paging support not working - LDAP: error code 12 - 00000057: LdapErr: DSID-0C09079A

When trying to run the code above I'm getting javax.naming.OperationNotSupportedException with the message:
[LDAP: error code 12 - 00000057: LdapErr: DSID-0C09079A, comment: Error processing control, data 0, v2580].
The first page is successfully retrieved and the exception is thrown only at second loop iteration.
public void pagedResults() {
PagedResultsCookie cookie = null;
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
int page = 1;
do {
logger.info("Starting Page: " + page);
PagedResultsDirContextProcessor processor = new PagedResultsDirContextProcessor(20, cookie);
List<String> lastNames = ldapTemplate.search("", initialFilter.encode(), searchControls, UserMapper.USER_MAPPER_VNT, processor);
for (String l : lastNames) {
logger.info(l);
}
cookie = processor.getCookie();
page = page + 1;
} while (null != cookie.getCookie());
}
However, when I remove Spring LDAP using pure implementation as above, it works!
try {
LdapContext ctx = new InitialLdapContext(env, null);
// Activate paged results
int pageSize = 5;
byte[] cookie = null;
ctx.setRequestControls(new Control[] { new PagedResultsControl(pageSize, Control.CRITICAL) });
int total;
do {
/* perform the search */
NamingEnumeration results = ctx .search("",
"(&(objectCategory=person)(objectClass=user)(SAMAccountName=vnt*))",
searchCtls);
/* for each entry print out name + all attrs and values */
while (results != null && results.hasMore()) {
SearchResult entry = (SearchResult) results.next();
System.out.println(entry.getName());
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (int i = 0; i < controls.length; i++) {
if (controls[i] instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i];
total = prrc.getResultSize();
if (total != 0) {
System.out.println("***************** END-OF-PAGE "
+ "(total : " + total
+ ") *****************\n");
} else {
System.out.println("***************** END-OF-PAGE "
+ "(total: unknown) ***************\n");
}
cookie = prrc.getCookie();
}
}
} else {
System.out.println("No controls were sent from the server");
}
// Re-activate paged results
ctx.setRequestControls(new Control[] { new PagedResultsControl(
pageSize, cookie, Control.CRITICAL) });
} while (cookie != null);
ctx.close();
} catch (NamingException e) {
System.err.println("PagedSearch failed.");
e.printStackTrace();
} catch (IOException ie) {
System.err.println("PagedSearch failed.");
ie.printStackTrace();
}
Any hints?
The bad thing about LDAP paged results is that they only work if the same underlying connection is used for all requests. The internals of Spring LDAP get a new connection for each LdapTemplate operation, unless you use the transactional support.
The easiest way to make sure the same connection will be used for a sequence of LDapTemplate operations is to use the transaction support, i.e. configure transactions for Spring LDAP and wrap the target method with a Transactional annotation.
I managed to make my example above work using SingleContextSource.doWithSingleContext approach.
However my scenario is different, my app is service oriented and the paged results as well as the cookie should be sent to an external client so that he decides to request next pages or not.
So as far as I can tell, spring-ldap does not support such case. I must use pure implementation so that I can keep track of the underlying connection during requests. Transaction support could help as well as SingleContextSource, but not among different requests.
#marthursson
Is there any plan in spring ldap to such support in the future?
I found I could use your first example (Spring) as long as I set the ignorePartialResultException property to true in my ldapTemplate configuration and put the #Transactional on my method as suggested.
you can replace ldapTemplate DirContext like this
ldapTemplate.setContextSource(new SingleContextSource(ldapContextSource().getReadWriteContext()));

Behavior of initial.min.cluster.size

Is Hazelcast always blocking in case initial.min.cluster.size is not reached? If not, under which situations is it not?
Details:
I use the following code to initialize hazelcast:
Config cfg = new Config();
cfg.setProperty("hazelcast.initial.min.cluster.size",Integer.
toString(minimumInitialMembersInHazelCluster)); //2 in this case
cfg.getGroupConfig().setName(clusterName);
NetworkConfig network = cfg.getNetworkConfig();
JoinConfig join = network.getJoin();
join.getMulticastConfig().setEnabled(false);
join.getTcpIpConfig().addMember("192.168.0.1").addMember("192.168.0.2").
addMember("192.168.0.3").addMember("192.168.0.4").
addMember("192.168.0.5").addMember("192.168.0.6").
addMember("192.168.0.7").setRequiredMember(null).setEnabled(true);
network.getInterfaces().setEnabled(true).addInterface("192.168.0.*");
join.getMulticastConfig().setMulticastTimeoutSeconds(MCSOCK_TIMEOUT/100);
hazelInst = Hazelcast.newHazelcastInstance(cfg);
distrDischargedTTGs = hazelInst.getList(clusterName);
and get log messages like
debug: starting Hazel pullExternal from Hazelcluster with 1 members.
Does that definitely mean there was another member that has joined and left already? It does not look like that would be the case from the log files of the other instance. Hence I wonder whether there are situtations where hazelInst = Hazelcast.newHazelcastInstance(cfg); does not block even though it is the only instance in the hazelcast cluster.
The newHazelcastInstance blocks till the clusters has the required number of members.
See the code below for how it is implemented:
private static void awaitMinimalClusterSize(HazelcastInstanceImpl hazelcastInstance, Node node, boolean firstMember)
throws InterruptedException {
final int initialMinClusterSize = node.groupProperties.INITIAL_MIN_CLUSTER_SIZE.getInteger();
while (node.getClusterService().getSize() < initialMinClusterSize) {
try {
hazelcastInstance.logger.info("HazelcastInstance waiting for cluster size of " + initialMinClusterSize);
//noinspection BusyWait
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
} catch (InterruptedException ignored) {
}
}
if (initialMinClusterSize > 1) {
if (firstMember) {
node.partitionService.firstArrangement();
} else {
Thread.sleep(TimeUnit.SECONDS.toMillis(3));
}
hazelcastInstance.logger.info("HazelcastInstance starting after waiting for cluster size of "
+ initialMinClusterSize);
}
}
If you set the logging on debug then perhaps you can see better what is happening. Member joining and leaving should already be visible under info.

LightSwitch Copy String to Clipboard

I've added System.Windows and Microsoft.LightSwitch.Threading; to get access to the clipboard.
When I run this program I get this error: Invalid cross-thread access.
Any ideas for getting the clipboard to work?
partial void btnCopyFaucets_Execute()
{
// Write your code here.
try
{
string CopyText, nManu, nProductCode, nFaucet;
Faucets cpyfaucet = this.FaucetsSearch.SelectedItem;
nManu = Convert.ToString(cpyfaucet.Manufacturer);
nProductCode = Convert.ToString(cpyfaucet.ProductCode);
nFaucet = Convert.ToString(cpyfaucet.Faucet);
CopyText = nManu + " " + nProductCode + " " + nFaucet;
// THIS IS WHERE THE COMPILER IS NOT HAPPY.
Clipboard.SetText(CopyText);
}
catch (Exception damnit)
{
MessageBox.Show(damnit.Message);
}
}
Try to use the Main dispatcher like this:
Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() =>
Clipboard.SetText(CopyText);
});
Maybe if it´s a web app you'll have permission issues after that.

Handling AccessViolation exception in try catch c#

How to catch the AccessViolation exception in try-catch block:
here is the code below:
public static BP GetBloodPressure(string vendorid, string productid)
{
BP Result = new BP();
try
{
GETBPData BPreadings = new GETBPData();
UInt16 VendorId = Convert.ToUInt16(vendorid, 16);
UInt16 ProductId = Convert.ToUInt16(productid, 16);
if (HealthMonitorData.HidDataTap_GetBloodPressure(VendorId, ProductId, ref BPreadings)) // error here
{
if (BPreadings.ucSystolic == 0 && BPreadings.ucDiastolic == 0 && BPreadings.DeviceId1 == 0 && BPreadings.DeviceId2 == 0 && BPreadings.ucPulse == 0)
{
Result = null;
}
else
{
Result.UcSystolic = BPreadings.ucSystolic;
Result.UcDiastolic = BPreadings.ucDiastolic;
Result.UcPulse = BPreadings.ucPulse;
Result.DeviceId1 = BPreadings.DeviceId1;
Result.DeviceId2 = BPreadings.DeviceId2;
}
}
}
catch (Exception ex)
{
}
return Result;
}
I am importing one dll to read the blood pressure values from the device. I have try to catch the exception but the control does not go beyond the "if" statement where the access violation exception is coming.
Kindly Suggest?
Thanks
Handling of AccessViolationExceptions and other corrupted state exceptions has been changed in .NET 4. Generally you should not catch these exceptions, so the runtime has been changed to reflect this. If you really need to catch these, you must annotate the code with the HandledProcessCorruptedStateExceptions attribute.
Please keep in mind, that the behavior was changed with good reason. Most applications will not be able to handle these exceptions in any meaningful way and thus should not catch them.
its HandleProcessCorruptedStateExceptions not HandleDProcessCorruptedStateExceptions

Resources