From 7b2f7547cf9088eb4308f80454b65a3dd12e81fa Mon Sep 17 00:00:00 2001 From: Stas Parshin <2172170+pengrad@users.noreply.github.com> Date: Fri, 6 Oct 2023 21:36:48 +0700 Subject: [PATCH] Return Cancellable from bot.execute with callback. Stop updates listener correctly #345 --- .../com/pengrad/telegrambot/Cancellable.java | 5 +++ .../com/pengrad/telegrambot/TelegramBot.java | 5 ++- .../telegrambot/impl/TelegramBotClient.java | 9 ++++- .../telegrambot/impl/UpdatesHandler.java | 11 +++++- .../pengrad/telegrambot/TelegramBotTest.java | 2 +- .../telegrambot/UpdatesListenerTest.java | 38 +++++++++++++++++-- 6 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 library/src/main/java/com/pengrad/telegrambot/Cancellable.java diff --git a/library/src/main/java/com/pengrad/telegrambot/Cancellable.java b/library/src/main/java/com/pengrad/telegrambot/Cancellable.java new file mode 100644 index 00000000..7379e363 --- /dev/null +++ b/library/src/main/java/com/pengrad/telegrambot/Cancellable.java @@ -0,0 +1,5 @@ +package com.pengrad.telegrambot; + +public interface Cancellable { + void cancel(); +} diff --git a/library/src/main/java/com/pengrad/telegrambot/TelegramBot.java b/library/src/main/java/com/pengrad/telegrambot/TelegramBot.java index fbecdad8..0d70351e 100644 --- a/library/src/main/java/com/pengrad/telegrambot/TelegramBot.java +++ b/library/src/main/java/com/pengrad/telegrambot/TelegramBot.java @@ -44,8 +44,8 @@ public , R extends BaseResponse> R execute(BaseReque return api.send(request); } - public , R extends BaseResponse> void execute(T request, Callback callback) { - api.send(request, callback); + public , R extends BaseResponse> Cancellable execute(T request, Callback callback) { + return api.send(request, callback); } public String getToken() { @@ -77,6 +77,7 @@ public void setUpdatesListener(UpdatesListener listener, ExceptionHandler except } public void setUpdatesListener(UpdatesListener listener, ExceptionHandler exceptionHandler, GetUpdates request) { + updatesHandler.stop(); updatesHandler.start(this, listener, exceptionHandler, request); } diff --git a/library/src/main/java/com/pengrad/telegrambot/impl/TelegramBotClient.java b/library/src/main/java/com/pengrad/telegrambot/impl/TelegramBotClient.java index ae0b95dc..7ceed383 100644 --- a/library/src/main/java/com/pengrad/telegrambot/impl/TelegramBotClient.java +++ b/library/src/main/java/com/pengrad/telegrambot/impl/TelegramBotClient.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; import com.pengrad.telegrambot.Callback; +import com.pengrad.telegrambot.Cancellable; import com.pengrad.telegrambot.model.request.InputFile; import com.pengrad.telegrambot.request.BaseRequest; import com.pengrad.telegrambot.response.BaseResponse; @@ -37,9 +38,11 @@ public TelegramBotClient(OkHttpClient client, Gson gson, String baseUrl) { this.clientWithTimeout = client; } - public , R extends BaseResponse> void send(final T request, final Callback callback) { + public , R extends BaseResponse> Cancellable send(final T request, final Callback callback) { OkHttpClient client = getOkHttpClient(request); - client.newCall(createRequest(request)).enqueue(new okhttp3.Callback() { + + Call call = client.newCall(createRequest(request)); + call.enqueue(new okhttp3.Callback() { @Override public void onResponse(Call call, Response response) { R result = null; @@ -64,6 +67,8 @@ public void onFailure(Call call, IOException e) { callback.onFailure(request, e); } }); + + return call::cancel; } public , R extends BaseResponse> R send(final BaseRequest request) { diff --git a/library/src/main/java/com/pengrad/telegrambot/impl/UpdatesHandler.java b/library/src/main/java/com/pengrad/telegrambot/impl/UpdatesHandler.java index e6cf6242..4a0f43fe 100644 --- a/library/src/main/java/com/pengrad/telegrambot/impl/UpdatesHandler.java +++ b/library/src/main/java/com/pengrad/telegrambot/impl/UpdatesHandler.java @@ -23,6 +23,7 @@ public class UpdatesHandler { private TelegramBot bot; private UpdatesListener listener; private ExceptionHandler exceptionHandler; + private Cancellable pendingRequest; private final long sleepTimeout; @@ -41,12 +42,16 @@ public void stop() { bot = null; listener = null; exceptionHandler = null; + if (pendingRequest != null) { + pendingRequest.cancel(); + pendingRequest = null; + } } private void getUpdates(GetUpdates request) { if (bot == null || listener == null) return; - bot.execute(request, new Callback() { + pendingRequest = bot.execute(request, new Callback() { @Override public void onResponse(GetUpdates request, GetUpdatesResponse response) { if (listener == null) return; @@ -83,6 +88,10 @@ public void onResponse(GetUpdates request, GetUpdatesResponse response) { @Override public void onFailure(GetUpdates request, IOException e) { + // TODO: better way to identify canceled request + if (e.getMessage().equals("Canceled")) { + return; + } if (exceptionHandler != null) { exceptionHandler.onException(new TelegramException(e)); } else { diff --git a/library/src/test/java/com/pengrad/telegrambot/TelegramBotTest.java b/library/src/test/java/com/pengrad/telegrambot/TelegramBotTest.java index fc2b3719..8b846b1a 100644 --- a/library/src/test/java/com/pengrad/telegrambot/TelegramBotTest.java +++ b/library/src/test/java/com/pengrad/telegrambot/TelegramBotTest.java @@ -485,7 +485,7 @@ public void getChat() throws MalformedURLException, URISyntaxException { chat = bot.execute(new GetChat(chatId)).chat(); assertNotNull(chat.firstName()); assertNull(chat.lastName()); - assertEquals("yo", chat.bio()); + assertNull(chat.bio()); assertTrue(chat.hasPrivateForwards()); chat = bot.execute(new GetChat(localGroup)).chat(); diff --git a/library/src/test/java/com/pengrad/telegrambot/UpdatesListenerTest.java b/library/src/test/java/com/pengrad/telegrambot/UpdatesListenerTest.java index 67d4032d..e442ae39 100644 --- a/library/src/test/java/com/pengrad/telegrambot/UpdatesListenerTest.java +++ b/library/src/test/java/com/pengrad/telegrambot/UpdatesListenerTest.java @@ -2,9 +2,11 @@ import com.pengrad.telegrambot.model.*; +import com.pengrad.telegrambot.request.GetUpdates; import org.junit.*; -import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; import java.time.*; import java.util.*; import java.util.concurrent.*; @@ -26,7 +28,7 @@ public static String token() { String token; try { Properties properties = new Properties(); - properties.load(new FileInputStream("local.properties")); + properties.load(Files.newInputStream(Paths.get("local.properties"))); token = properties.getProperty("TEST_TOKEN"); } catch (Exception e) { token = System.getenv("TEST_TOKEN"); @@ -41,6 +43,34 @@ public void initBot() { bot = new TelegramBot.Builder(token()).debug().build(); } + @Test + public void setAndRemoveListener() throws InterruptedException { + // Two simultaneous getUpdates requests throw error + // 409 Conflict: terminated by other getUpdates request; make sure that only one bot instance is running + CountDownLatch latch = new CountDownLatch(1); + AtomicReference exception = new AtomicReference<>(); + UpdatesListener listener = updates -> UpdatesListener.CONFIRMED_UPDATES_NONE; + ExceptionHandler exceptionHandler = e -> { + System.out.println("Exception in handler: " + e.getMessage()); + exception.set(e); + latch.countDown(); + }; + bot.setUpdatesListener(listener, exceptionHandler); + // trigger another getUpdates and cancel first one + bot.setUpdatesListener(listener, exceptionHandler); + // cancel second one + bot.removeGetUpdatesListener(); + + UpdatesListener listener2 = updates -> { + latch.countDown(); + return UpdatesListener.CONFIRMED_UPDATES_ALL; + }; + bot.setUpdatesListener(listener2, exceptionHandler, new GetUpdates().offset(-1).allowedUpdates()); + latch.await(3, TimeUnit.SECONDS); + bot.removeGetUpdatesListener(); + assertNull(exception.get()); + } + @Test public void receiveUpdates() throws InterruptedException { withLatch(1, latch -> { @@ -55,8 +85,8 @@ public void receiveUpdates() throws InterruptedException { .message("") .build(); response = response.newBuilder().body(ResponseBody - .create(MediaType.parse("application/json"), - "{\"ok\":true,\"result\":[{\"update_id\":" + i.getAndIncrement() + "}]}" + .create("{\"ok\":true,\"result\":[{\"update_id\":" + i.getAndIncrement() + "}]}", + MediaType.parse("application/json") )).build(); return response; })