diff --git a/.gitignore b/.gitignore index 9ff37ea..ac264bf 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,7 @@ parent/.classpath tdlight-java/target-snapshot/ tdlight-java-8/target-snapshot/ +/example-tdlight-session/data/db.sqlite +/example-tdlight-session/data/db.sqlite-shm +/example-tdlight-session/data/db.sqlite-wal +/example-tdlight-session/data/td.binlog diff --git a/tdlight-java/src/main/java/it/tdlight/AutoCleaningTelegramClient.java b/tdlight-java/src/main/java/it/tdlight/AutoCleaningTelegramClient.java index 9f9cbc8..7c64237 100644 --- a/tdlight-java/src/main/java/it/tdlight/AutoCleaningTelegramClient.java +++ b/tdlight-java/src/main/java/it/tdlight/AutoCleaningTelegramClient.java @@ -6,11 +6,14 @@ import it.tdlight.jni.TdApi.Object; import it.tdlight.jni.TdApi.Update; import it.tdlight.util.CleanSupport; import it.tdlight.util.CleanSupport.CleanableSupport; +import java.util.Map; import java.util.function.LongSupplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MarkerFactory; class AutoCleaningTelegramClient implements TelegramClient { - - private final TelegramClient client; + private final InternalClient client; private volatile CleanableSupport cleanable; AutoCleaningTelegramClient(InternalClientsState state) { @@ -18,7 +21,12 @@ class AutoCleaningTelegramClient implements TelegramClient { } public void onClientRegistered(int clientId, LongSupplier nextQueryIdSupplier) { - Runnable shutDown = () -> NativeClientAccess.send(clientId, nextQueryIdSupplier.getAsLong(), new TdApi.Close()); + Runnable shutDown = () -> { + Logger logger = LoggerFactory.getLogger(TelegramClient.class); + logger.debug(MarkerFactory.getMarker("TG"), "The client is being shut down automatically"); + long reqId = nextQueryIdSupplier.getAsLong(); + NativeClientAccess.send(clientId, reqId, new TdApi.Close()); + }; Thread shutdownHook = new Thread(shutDown); Runtime.getRuntime().addShutdownHook(shutdownHook); cleanable = CleanSupport.register(this, shutDown); diff --git a/tdlight-java/src/main/java/it/tdlight/ClientFactory.java b/tdlight-java/src/main/java/it/tdlight/ClientFactory.java index f74a2de..fe19593 100644 --- a/tdlight-java/src/main/java/it/tdlight/ClientFactory.java +++ b/tdlight-java/src/main/java/it/tdlight/ClientFactory.java @@ -115,9 +115,9 @@ public class ClientFactory implements AutoCloseable { } if (isClosed) { - logger.trace("Removing Client {} from event handlers", clientId); + logger.debug("Removing Client {} from event handlers", clientId); state.removeClientEventHandlers(clientId); - logger.trace("Removed Client {} from event handlers", clientId); + logger.debug("Removed Client {} from event handlers", clientId); } } diff --git a/tdlight-java/src/main/java/it/tdlight/ClientRegistrationEventHandler.java b/tdlight-java/src/main/java/it/tdlight/ClientRegistrationEventHandler.java index f4a4475..2e97728 100644 --- a/tdlight-java/src/main/java/it/tdlight/ClientRegistrationEventHandler.java +++ b/tdlight-java/src/main/java/it/tdlight/ClientRegistrationEventHandler.java @@ -1,7 +1,9 @@ package it.tdlight; +import java.util.Map; import java.util.function.LongSupplier; -public interface ClientRegistrationEventHandler { +interface ClientRegistrationEventHandler { + void onClientRegistered(int clientId, LongSupplier nextQueryIdSupplier); } diff --git a/tdlight-java/src/main/java/it/tdlight/Init.java b/tdlight-java/src/main/java/it/tdlight/Init.java index 6656d1f..a63c77a 100644 --- a/tdlight-java/src/main/java/it/tdlight/Init.java +++ b/tdlight-java/src/main/java/it/tdlight/Init.java @@ -42,38 +42,42 @@ public final class Init { */ public static void init() throws UnsupportedNativeLibraryException { if (!started) { + boolean shouldStart = false; synchronized (Init.class) { if (!started) { - Native.loadNativesInternal(); - ConstructorDetector.init(); - try { - NativeClientAccess.execute(new SetLogVerbosityLevel(3)); - Log.setLogMessageHandler(3, (verbosityLevel, message) -> { - switch (verbosityLevel) { - case -1: - case 0: - case 1: - LOG.error(message); - break; - case 2: - LOG.warn(message); - break; - case 3: - LOG.info(message); - break; - case 4: - LOG.debug(message); - break; - default: - LOG.trace(message); - break; - } - }); - NativeClientAccess.execute(new SetLogStream(new LogStreamEmpty())); - } catch (Throwable ex) { - LOG.error("Can't set verbosity level on startup", ex); - } started = true; + shouldStart = true; + } + } + if (shouldStart) { + Native.loadNativesInternal(); + ConstructorDetector.init(); + try { + NativeClientAccess.execute(new SetLogVerbosityLevel(3)); + Log.setLogMessageHandler(3, (verbosityLevel, message) -> { + switch (verbosityLevel) { + case -1: + case 0: + case 1: + LOG.error(message); + break; + case 2: + LOG.warn(message); + break; + case 3: + LOG.info(message); + break; + case 4: + LOG.debug(message); + break; + default: + LOG.trace(message); + break; + } + }); + NativeClientAccess.execute(new SetLogStream(new LogStreamEmpty())); + } catch (Throwable ex) { + LOG.error("Can't set verbosity level on startup", ex); } } } diff --git a/tdlight-java/src/main/java/it/tdlight/InternalClient.java b/tdlight-java/src/main/java/it/tdlight/InternalClient.java index 6f0b5a9..a9a2acd 100644 --- a/tdlight-java/src/main/java/it/tdlight/InternalClient.java +++ b/tdlight-java/src/main/java/it/tdlight/InternalClient.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; @@ -15,8 +17,8 @@ import org.slf4j.MarkerFactory; final class InternalClient implements ClientEventsHandler, TelegramClient { - private static final Marker TG_MARKER = MarkerFactory.getMarker("TG"); - private static final Logger logger = LoggerFactory.getLogger(TelegramClient.class); + static final Marker TG_MARKER = MarkerFactory.getMarker("TG"); + static final Logger logger = LoggerFactory.getLogger(TelegramClient.class); private ClientRegistrationEventHandler clientRegistrationEventHandler; private final Map> handlers = new ConcurrentHashMap<>(); @@ -80,11 +82,11 @@ final class InternalClient implements ClientEventsHandler, TelegramClient { } private void handleClose() { - logger.trace(TG_MARKER, "Received close"); + logger.debug(TG_MARKER, "Received close"); handlers.forEach((eventId, handler) -> handleResponse(eventId, new TdApi.Error(500, "Instance closed"), handler)); handlers.clear(); - logger.info(TG_MARKER, "Client closed {}", clientId); + logger.debug(TG_MARKER, "Client closed {}", clientId); } /** @@ -98,7 +100,7 @@ final class InternalClient implements ClientEventsHandler, TelegramClient { handleException(handler.getExceptionHandler(), cause); } } else { - logger.error(TG_MARKER, "Unknown event id \"{}\", the event has been dropped! {}", eventId, event); + logger.trace(TG_MARKER, "Client {}, request event id is not registered \"{}\", the following response has been dropped. {}", clientId, eventId, event); } } @@ -106,7 +108,7 @@ final class InternalClient implements ClientEventsHandler, TelegramClient { * Handles a response or an update */ private void handleEvent(long eventId, TdApi.Object event) { - logger.trace(TG_MARKER, "Received response {}: {}", eventId, event); + logger.trace(TG_MARKER, "Client {}, response received for request {}: {}", clientId, eventId, event); if (updatesHandler != null || updateHandler == null) { throw new IllegalStateException(); } @@ -164,18 +166,23 @@ final class InternalClient implements ClientEventsHandler, TelegramClient { logger.info(TG_MARKER, "Registered new client {}", clientId); // Send a dummy request to start TDLib - this.send(new TdApi.GetOption("version"), (result) -> {}, ex -> {}); + logger.debug(TG_MARKER, "Sending dummy startup request as client {}", clientId); + TdApi.Function dummyRequest = new TdApi.GetOption("version"); + this.send(dummyRequest, null, null); + // test Client.execute + this.execute(new TdApi.GetTextEntities("@telegram /test_command https://telegram.org telegram.me @gif @test")); } @Override public void send(Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { - logger.trace(TG_MARKER, "Trying to send {}", query); + logger.trace(TG_MARKER, "Trying to send async request {}", query); // Handle special requests TdApi.Object specialResult = tryHandleSpecial(query); if (specialResult != null) { + logger.trace(TG_MARKER, "Handling special result for async request {}: {}", query, specialResult); if (resultHandler != null) { resultHandler.onResult(specialResult); } @@ -191,11 +198,12 @@ final class InternalClient implements ClientEventsHandler, TelegramClient { @Override public TdApi.Object execute(Function query) { - logger.trace(TG_MARKER, "Trying to execute {}", query); + logger.trace(TG_MARKER, "Trying to execute sync request {}", query); // Handle special requests TdApi.Object specialResult = tryHandleSpecial(query); if (specialResult != null) { + logger.trace(TG_MARKER, "Handling special result for sync request {}: {}", query, specialResult); return specialResult; } diff --git a/tdlight-java/src/main/java/it/tdlight/InternalClientsState.java b/tdlight-java/src/main/java/it/tdlight/InternalClientsState.java index 533e45f..2b0bbdc 100644 --- a/tdlight-java/src/main/java/it/tdlight/InternalClientsState.java +++ b/tdlight-java/src/main/java/it/tdlight/InternalClientsState.java @@ -2,6 +2,7 @@ package it.tdlight; import io.atlassian.util.concurrent.CopyOnWriteMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -13,11 +14,11 @@ public class InternalClientsState { static final int STATE_STOPPED = 4; private final AtomicInteger runState = new AtomicInteger(); private final AtomicLong currentQueryId = new AtomicLong(); - private final Map registeredClientEventHandlers = CopyOnWriteMap.newHashMap(); + private final Map registeredClientEventHandlers = new ConcurrentHashMap<>(); public long getNextQueryId() { - return currentQueryId.updateAndGet(value -> (value >= Long.MAX_VALUE ? 0 : value) + 1); + return currentQueryId.updateAndGet(value -> (value == Long.MAX_VALUE ? 0 : value) + 1); } public void registerClient(int clientId, ClientEventsHandler internalClient) { diff --git a/tdlight-java/src/main/java/it/tdlight/InternalReactiveClient.java b/tdlight-java/src/main/java/it/tdlight/InternalReactiveClient.java index 94c1746..bfa17f0 100644 --- a/tdlight-java/src/main/java/it/tdlight/InternalReactiveClient.java +++ b/tdlight-java/src/main/java/it/tdlight/InternalReactiveClient.java @@ -80,11 +80,11 @@ final class InternalReactiveClient implements ClientEventsHandler, ReactiveTeleg * This method will be called exactly once */ private void handleClose() { - logger.trace(TG_MARKER, "Received close"); + logger.debug(TG_MARKER, "Received close"); try { Runtime.getRuntime().removeShutdownHook(shutdownHook); } catch (IllegalStateException ignored) { - logger.trace(TG_MARKER, "Can't remove shutdown hook because the JVM is already shutting down"); + logger.debug(TG_MARKER, "Can't remove shutdown hook because the JVM is already shutting down"); } TdApi.Error instanceClosedError = new Error(500, "Instance closed"); handlers.forEach((eventId, handler) -> this.handleResponse(eventId, instanceClosedError, handler)); @@ -186,7 +186,7 @@ final class InternalReactiveClient implements ClientEventsHandler, ReactiveTeleg subscriber.onNext(new TdApi.Ok()); subscriber.onComplete(); } else if (clientId == null) { - logger.trace(TG_MARKER, + logger.debug(TG_MARKER, "Can't send a request to TDLib before calling \"createAndRegisterClient\" function!" ); subscriber.onError(new IllegalStateException( diff --git a/tdlight-java/src/main/java/it/tdlight/ResponseReceiver.java b/tdlight-java/src/main/java/it/tdlight/ResponseReceiver.java index 036c70f..5883df1 100644 --- a/tdlight-java/src/main/java/it/tdlight/ResponseReceiver.java +++ b/tdlight-java/src/main/java/it/tdlight/ResponseReceiver.java @@ -15,9 +15,12 @@ import java.util.Objects; import java.util.Set; import java.util.StringJoiner; import java.util.concurrent.CountDownLatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; abstract class ResponseReceiver extends Thread implements AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(ResponseReceiver.class); private static final String FLAG_USE_OPTIMIZED_DISPATCHER = "tdlight.dispatcher.use_optimized_dispatcher"; private static final boolean USE_OPTIMIZED_DISPATCHER = Boolean.parseBoolean(System.getProperty(FLAG_USE_OPTIMIZED_DISPATCHER, "true")); @@ -59,6 +62,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable { @Override public void run() { + LOG.debug("ResponseReceiver is now running"); int[] sortIndex; final SimpleIntQueue closedClients = new SimpleIntQueue(); try { @@ -66,6 +70,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable { while (!(interrupted = Thread.interrupted()) && registeredClients.length > 0) { // Timeout is expressed in seconds int resultsCount = receive(clientIds, eventIds, events, 2.0); + LOG.trace("Received {} events", resultsCount); if (resultsCount <= 0) { SpinWaitSupport.onSpinWait(); @@ -207,12 +212,15 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable { } } + LOG.trace("ResponseReceiver will no longer process updates"); + if (interrupted) { for (int clientId : registeredClients) { eventsHandler.handleClientEvents(clientId, true, clientEventIds, clientEvents, 0, 0); } } } finally { + LOG.debug("ResponseReceiver stopped"); this.closeWait.countDown(); } } @@ -266,6 +274,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable { public void close() throws InterruptedException { this.closeWait.await(); if (registeredClients.length == 0) { + LOG.debug("Interrupting response receiver"); ResponseReceiver.this.interrupt(); } } diff --git a/tdlight-java/src/main/java/it/tdlight/client/SequentialRequestsExecutor.java b/tdlight-java/src/main/java/it/tdlight/client/SequentialRequestsExecutor.java index 0a2161f..3be0c8a 100644 --- a/tdlight-java/src/main/java/it/tdlight/client/SequentialRequestsExecutor.java +++ b/tdlight-java/src/main/java/it/tdlight/client/SequentialRequestsExecutor.java @@ -9,6 +9,7 @@ class SequentialRequestsExecutor implements Executor { private final Executor executor = Executors.newSingleThreadExecutor(r -> { final Thread thread = new Thread(r); + thread.setName("TDLight user input request"); thread.setDaemon(true); return thread; }); diff --git a/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClient.java b/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClient.java index 70b0df0..f4304c5 100644 --- a/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClient.java +++ b/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClient.java @@ -7,7 +7,6 @@ import it.tdlight.Init; import it.tdlight.ResultHandler; import it.tdlight.TelegramClient; import it.tdlight.jni.TdApi.Update; -import it.tdlight.util.MapUtils; import it.tdlight.util.UnsupportedNativeLibraryException; import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi.ChatListArchive; @@ -17,7 +16,6 @@ import it.tdlight.jni.TdApi.User; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; -import java.util.IdentityHashMap; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -45,7 +43,7 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra private final TelegramClient client; private ClientInteraction clientInteraction; private final TDLibSettings settings; - private AuthenticationSupplier authenticationData; + private final AuthenticationSupplier authenticationData; private final CopyOnWriteMap> commandHandlers = CopyOnWriteMap.newHashMap(); private final CopyOnWriteMap, Void> updateHandlers = CopyOnWriteMap.newHashMap(); @@ -64,7 +62,8 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra Set> updateHandlers, Set updateExceptionHandlers, Set defaultExceptionHandlers, - ClientInteraction clientInteraction) { + ClientInteraction clientInteraction, + AuthenticationSupplier authenticationData) { this.client = clientFactory.createClient(); this.settings = Objects.requireNonNull(settings, "TDLight client settings are null"); @@ -115,6 +114,9 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra this.addUpdateHandler(TdApi.UpdateAuthorizationState.class, this.meGetter = new AuthorizationStateReadyGetMe(client, mainChatsLoader, archivedChatsLoader)); this.addUpdateHandler(TdApi.UpdateNewMessage.class, new CommandsHandler(client, this.commandHandlers, this::getMe)); + this.authenticationData = authenticationData; + createDirectories(); + client.initialize(this::handleUpdate, this::handleUpdateException, this::handleDefaultException); } private void handleUpdate(TdApi.Object update) { @@ -196,15 +198,6 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra this.defaultExceptionHandlers.put(defaultExceptionHandlers, null); } - /** - * Start the client - */ - public void start(AuthenticationSupplier authenticationData) { - this.authenticationData = authenticationData; - createDirectories(); - client.initialize(this::handleUpdate, this::handleUpdateException, this::handleDefaultException); - } - private void createDirectories() { try { if (Files.notExists(settings.getDatabaseDirectoryPath())) { diff --git a/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClientBuilder.java b/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClientBuilder.java index 8b5824f..e3586f8 100644 --- a/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClientBuilder.java +++ b/tdlight-java/src/main/java/it/tdlight/client/SimpleTelegramClientBuilder.java @@ -62,17 +62,16 @@ public final class SimpleTelegramClientBuilder implements MutableTelegramClient * Build and start the client * @return Telegram client */ - public SimpleTelegramClient build(AuthenticationSupplier authenticationData) { - SimpleTelegramClient client = new SimpleTelegramClient(clientManager, + public SimpleTelegramClient build(AuthenticationSupplier authenticationData) { + return new SimpleTelegramClient(clientManager, clientSettings, commandHandlers, updateHandlers, updateExceptionHandlers, defaultExceptionHandlers, - clientInteraction + clientInteraction, + authenticationData ); - client.start(authenticationData); - return client; } } diff --git a/tdlight-java/src/main/java/it/tdlight/util/Native.java b/tdlight-java/src/main/java/it/tdlight/util/Native.java index 7f111f9..b8dcb60 100644 --- a/tdlight-java/src/main/java/it/tdlight/util/Native.java +++ b/tdlight-java/src/main/java/it/tdlight/util/Native.java @@ -34,7 +34,7 @@ public final class Native { * Internal util */ public static void loadNativesInternal() throws UnsupportedNativeLibraryException { - loadLibrary("tdlight"); + loadLibrary("tdjni"); } private static final Logger logger = LoggerFactory.getLogger(Native.class);