X-Git-Url: https://git.ktnx.net/?p=mobile-ledger.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fnet%2Fktnx%2Fmobileledger%2Fasync%2FSendTransactionTask.java;h=0e700cca9a510be8e210fafccbe548b88c2a427a;hp=f4c7ce1fdd48ced4e421e6ea0edad468fc66a0f5;hb=HEAD;hpb=bd5da50ef980c0c9657ec1e9c3e681ab5092f438 diff --git a/app/src/main/java/net/ktnx/mobileledger/async/SendTransactionTask.java b/app/src/main/java/net/ktnx/mobileledger/async/SendTransactionTask.java index f4c7ce1f..0e700cca 100644 --- a/app/src/main/java/net/ktnx/mobileledger/async/SendTransactionTask.java +++ b/app/src/main/java/net/ktnx/mobileledger/async/SendTransactionTask.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Damyan Ivanov. + * Copyright © 2023 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -17,18 +17,21 @@ package net.ktnx.mobileledger.async; -import android.os.AsyncTask; -import android.util.Log; +import static net.ktnx.mobileledger.utils.Logger.debug; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; +import android.util.Log; -import net.ktnx.mobileledger.json.ParsedLedgerTransaction; +import net.ktnx.mobileledger.db.Profile; +import net.ktnx.mobileledger.json.API; +import net.ktnx.mobileledger.json.ApiNotSupportedException; +import net.ktnx.mobileledger.json.Gateway; import net.ktnx.mobileledger.model.LedgerTransaction; import net.ktnx.mobileledger.model.LedgerTransactionAccount; -import net.ktnx.mobileledger.model.MobileLedgerProfile; import net.ktnx.mobileledger.utils.Globals; +import net.ktnx.mobileledger.utils.Logger; +import net.ktnx.mobileledger.utils.Misc; import net.ktnx.mobileledger.utils.NetworkUtil; +import net.ktnx.mobileledger.utils.SimpleDate; import net.ktnx.mobileledger.utils.UrlEncodedFormData; import java.io.BufferedReader; @@ -44,47 +47,71 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static android.os.SystemClock.sleep; -import static net.ktnx.mobileledger.utils.Logger.debug; +/* TODO: get rid of the custom session/cookie and auth code? + * (the last problem with the POST was the missing content-length header) + * This will resolve itself when hledger-web 1.14+ is released with Debian/stable, + * at which point the HTML form emulation can be dropped entirely + */ -public class SendTransactionTask extends AsyncTask { +public class SendTransactionTask extends Thread { private final TaskCallback taskCallback; + private final Profile mProfile; + private final boolean simulate; + private final LedgerTransaction transaction; protected String error; private String token; private String session; - private LedgerTransaction ltr; - private MobileLedgerProfile mProfile; - public SendTransactionTask(TaskCallback callback, MobileLedgerProfile profile) { + public SendTransactionTask(TaskCallback callback, Profile profile, + LedgerTransaction transaction, boolean simulate) { taskCallback = callback; mProfile = profile; + this.transaction = transaction; + this.simulate = simulate; } - private boolean sendOK() throws IOException { + private void sendOK(API apiVersion) throws IOException, ApiNotSupportedException { HttpURLConnection http = NetworkUtil.prepareConnection(mProfile, "add"); http.setRequestMethod("PUT"); http.setRequestProperty("Content-Type", "application/json"); http.setRequestProperty("Accept", "*/*"); - ParsedLedgerTransaction jsonTransaction; - jsonTransaction = ltr.toParsedLedgerTransaction(); - ObjectMapper mapper = new ObjectMapper(); - ObjectWriter writer = mapper.writerFor(ParsedLedgerTransaction.class); - String body = writer.writeValueAsString(jsonTransaction); + Gateway gateway = Gateway.forApiVersion(apiVersion); + String body = gateway.transactionSaveRequest(transaction); + + Logger.debug("network", "Sending using API " + apiVersion); + sendRequest(http, body); + } + private void sendRequest(HttpURLConnection http, String body) + throws IOException, ApiNotSupportedException { + if (simulate) { + debug("network", "The request would be: " + body); + try { + Thread.sleep(1500); + if (Math.random() > 0.3) + throw new RuntimeException("Simulated test exception"); + } + catch (InterruptedException ex) { + Logger.debug("network", ex.toString()); + } + + return; + } byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8); http.setDoOutput(true); http.setDoInput(true); http.addRequestProperty("Content-Length", String.valueOf(bodyBytes.length)); - debug("network", "request header: " + http.getRequestProperties().toString()); + debug("network", "request header: " + http.getRequestProperties() + .toString()); try (OutputStream req = http.getOutputStream()) { debug("network", "Request body: " + body); req.write(bodyBytes); final int responseCode = http.getResponseCode(); - debug("network", - String.format("Response: %d %s", responseCode, http.getResponseMessage())); + debug("network", String.format(Locale.US, "Response: %d %s", responseCode, + http.getResponseMessage())); try (InputStream resp = http.getErrorStream()) { @@ -92,8 +119,25 @@ public class SendTransactionTask extends AsyncTask= 2) - throw new IOException(String.format("aborting after %d tries", tried)); - sleep(100); - } + final API profileApiVersion = API.valueOf(mProfile.getApiVersion()); + switch (profileApiVersion) { + case auto: + boolean sendOK = false; + for (API ver : API.allVersions) { + Logger.debug("network", "Trying version " + ver); + try { + sendOK(ver); + sendOK = true; + Logger.debug("network", "Version " + ver + " request succeeded"); + + break; + } + catch (ApiNotSupportedException e) { + Logger.debug("network", "Version " + ver + " seems not supported"); + } + } + + if (!sendOK) { + Logger.debug("network", "Trying HTML form emulation"); + legacySendOkWithRetry(); + } + break; + case html: + legacySendOkWithRetry(); + break; + case v1_14: + case v1_15: + case v1_19_1: + case v1_23: + sendOK(profileApiVersion); + break; + default: + throw new IllegalStateException("Unexpected API version: " + profileApiVersion); } } - catch (Exception e) { + catch (ApiNotSupportedException | Exception e) { e.printStackTrace(); error = e.getMessage(); } - return null; + Misc.onMainThread(() -> taskCallback.onTransactionSaveDone(error, transaction)); } - - @Override - protected void onPostExecute(Void aVoid) { - super.onPostExecute(aVoid); - taskCallback.done(error); + private void legacySendOkWithRetry() throws IOException { + int tried = 0; + while (!legacySendOK()) { + tried++; + if (tried >= 2) + throw new IOException(String.format("aborting after %d tries", tried)); + try { + sleep(100); + } + catch (InterruptedException e) { + e.printStackTrace(); + break; + } + } } -} +} \ No newline at end of file