New api
This commit is contained in:
parent
4d51b63737
commit
0fd22c61f4
2
dependencies/tdlight
vendored
2
dependencies/tdlight
vendored
@ -1 +1 @@
|
|||||||
Subproject commit f5ecc4b89655ddfaa1e05b77d6740e8d9cdfcaea
|
Subproject commit 85bf271fb827a31b68d761a06a92972aa6548338
|
10
src/main/java/it/tdlight/common/ClientEventsHandler.java
Normal file
10
src/main/java/it/tdlight/common/ClientEventsHandler.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
import it.tdlight.jni.TdApi.Object;
|
||||||
|
|
||||||
|
public interface ClientEventsHandler {
|
||||||
|
|
||||||
|
int getClientId();
|
||||||
|
|
||||||
|
void handleEvents(boolean isClosed, long[] eventIds, Object[] events);
|
||||||
|
}
|
38
src/main/java/it/tdlight/common/CommonClient.java
Normal file
38
src/main/java/it/tdlight/common/CommonClient.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
public abstract class CommonClient {
|
||||||
|
|
||||||
|
protected abstract String getImplementationName();
|
||||||
|
|
||||||
|
|
||||||
|
private InternalClientManager getClientManager() {
|
||||||
|
// ClientManager is singleton:
|
||||||
|
return InternalClientManager.get(getImplementationName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TelegramClient create(ResultHandler updateHandler,
|
||||||
|
ExceptionHandler updateExceptionHandler,
|
||||||
|
ExceptionHandler defaultExceptionHandler) {
|
||||||
|
InternalClient client = new InternalClient(getClientManager(),
|
||||||
|
updateHandler,
|
||||||
|
updateExceptionHandler,
|
||||||
|
defaultExceptionHandler
|
||||||
|
);
|
||||||
|
return create(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TelegramClient create(UpdatesHandler updatesHandler,
|
||||||
|
ExceptionHandler updateExceptionHandler,
|
||||||
|
ExceptionHandler defaultExceptionHandler) {
|
||||||
|
InternalClient client = new InternalClient(getClientManager(),
|
||||||
|
updatesHandler,
|
||||||
|
updateExceptionHandler,
|
||||||
|
defaultExceptionHandler
|
||||||
|
);
|
||||||
|
return create(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TelegramClient create(InternalClient internalClient) {
|
||||||
|
return internalClient;
|
||||||
|
}
|
||||||
|
}
|
7
src/main/java/it/tdlight/common/EventsHandler.java
Normal file
7
src/main/java/it/tdlight/common/EventsHandler.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
import it.tdlight.jni.TdApi.Object;
|
||||||
|
|
||||||
|
public interface EventsHandler {
|
||||||
|
void handleClientEvents(int clientId, boolean isClosed, long[] clientEventIds, Object[] clientEvents);
|
||||||
|
}
|
19
src/main/java/it/tdlight/common/Handler.java
Normal file
19
src/main/java/it/tdlight/common/Handler.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
public class Handler {
|
||||||
|
private final ResultHandler resultHandler;
|
||||||
|
private final ExceptionHandler exceptionHandler;
|
||||||
|
|
||||||
|
public Handler(ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
|
||||||
|
this.resultHandler = resultHandler;
|
||||||
|
this.exceptionHandler = exceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultHandler getResultHandler() {
|
||||||
|
return resultHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExceptionHandler getExceptionHandler() {
|
||||||
|
return exceptionHandler;
|
||||||
|
}
|
||||||
|
}
|
21
src/main/java/it/tdlight/common/IntSwapper.java
Normal file
21
src/main/java/it/tdlight/common/IntSwapper.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.Swapper;
|
||||||
|
|
||||||
|
class IntSwapper implements Swapper {
|
||||||
|
|
||||||
|
private final int[] array;
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
public IntSwapper(int[] array) {
|
||||||
|
this.array = array;
|
||||||
|
tmp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void swap(int a, int b) {
|
||||||
|
tmp = array[a];
|
||||||
|
array[a] = array[b];
|
||||||
|
array[b] = tmp;
|
||||||
|
}
|
||||||
|
}
|
162
src/main/java/it/tdlight/common/InternalClient.java
Normal file
162
src/main/java/it/tdlight/common/InternalClient.java
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
import it.tdlight.jni.TdApi.Error;
|
||||||
|
import it.tdlight.jni.TdApi.Function;
|
||||||
|
import it.tdlight.jni.TdApi.Object;
|
||||||
|
import it.tdlight.jni.TdApi.Update;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class InternalClient implements ClientEventsHandler, TelegramClient {
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Long, Handler> handlers = new ConcurrentHashMap<Long, Handler>();
|
||||||
|
|
||||||
|
private final int clientId;
|
||||||
|
private final InternalClientManager clientManager;
|
||||||
|
private final Handler updateHandler;
|
||||||
|
private final MultiHandler updatesHandler;
|
||||||
|
private final ExceptionHandler defaultExceptionHandler;
|
||||||
|
|
||||||
|
private volatile boolean isClosed;
|
||||||
|
|
||||||
|
public InternalClient(InternalClientManager clientManager,
|
||||||
|
ResultHandler updateHandler,
|
||||||
|
ExceptionHandler updateExceptionHandler,
|
||||||
|
ExceptionHandler defaultExceptionHandler) {
|
||||||
|
this.updateHandler = new Handler(updateHandler, updateExceptionHandler);
|
||||||
|
this.updatesHandler = null;
|
||||||
|
this.clientManager = clientManager;
|
||||||
|
this.clientId = NativeClientAccess.create();
|
||||||
|
this.defaultExceptionHandler = defaultExceptionHandler;
|
||||||
|
|
||||||
|
clientManager.registerClient(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InternalClient(InternalClientManager clientManager,
|
||||||
|
UpdatesHandler updatesHandler,
|
||||||
|
ExceptionHandler updateExceptionHandler,
|
||||||
|
ExceptionHandler defaultExceptionHandler) {
|
||||||
|
this.updateHandler = null;
|
||||||
|
this.updatesHandler = new MultiHandler(updatesHandler, updateExceptionHandler);
|
||||||
|
this.clientManager = clientManager;
|
||||||
|
this.clientId = NativeClientAccess.create();
|
||||||
|
this.defaultExceptionHandler = defaultExceptionHandler;
|
||||||
|
|
||||||
|
clientManager.registerClient(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getClientId() {
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleEvents(boolean isClosed, long[] eventIds, Object[] events) {
|
||||||
|
if (updatesHandler != null) {
|
||||||
|
LongArrayList idsToFilter = new LongArrayList(eventIds);
|
||||||
|
ObjectArrayList<Object> eventsToFilter = new ObjectArrayList<>(events);
|
||||||
|
|
||||||
|
for (int i = eventIds.length - 1; i >= 0; i--) {
|
||||||
|
if (eventIds[i] != 0) {
|
||||||
|
idsToFilter.removeLong(i);
|
||||||
|
eventsToFilter.remove(i);
|
||||||
|
|
||||||
|
long eventId = eventIds[i];
|
||||||
|
Object event = events[i];
|
||||||
|
|
||||||
|
Handler handler = handlers.remove(eventId);
|
||||||
|
handleResponse(eventId, event, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventsToFilter.removeIf(event -> {
|
||||||
|
if (event instanceof Error) {
|
||||||
|
handleException(updatesHandler.getExceptionHandler(), new TDLibException((Error) event));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
ObjectArrayList<Update> updates = new ObjectArrayList<>(eventsToFilter.size());
|
||||||
|
for (Object object : eventsToFilter) {
|
||||||
|
updates.add((Update) object);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
updatesHandler.getUpdatesHandler().onUpdates(updates);
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
handleException(updatesHandler.getExceptionHandler(), cause);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < eventIds.length; i++) {
|
||||||
|
handleEvent(eventIds[i], events[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isClosed) {
|
||||||
|
this.isClosed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles only a response (not an update!)
|
||||||
|
*/
|
||||||
|
private void handleResponse(long eventId, Object event, Handler handler) {
|
||||||
|
if (handler != null) {
|
||||||
|
try {
|
||||||
|
if (event instanceof Error) {
|
||||||
|
handleException(handler.getExceptionHandler(), new TDLibException((Error) event));
|
||||||
|
} else {
|
||||||
|
handler.getResultHandler().onResult(event);
|
||||||
|
}
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
handleException(handler.getExceptionHandler(), cause);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println("Unknown event id " + eventId + ", the event has been dropped!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a response or an update
|
||||||
|
*/
|
||||||
|
private void handleEvent(long eventId, Object event) {
|
||||||
|
if (updatesHandler != null || updateHandler == null) throw new IllegalStateException();
|
||||||
|
Handler handler = eventId == 0 ? updateHandler : handlers.remove(eventId);
|
||||||
|
handleResponse(eventId, event, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleException(ExceptionHandler exceptionHandler, Throwable cause) {
|
||||||
|
if (exceptionHandler == null) {
|
||||||
|
exceptionHandler = defaultExceptionHandler;
|
||||||
|
}
|
||||||
|
if (exceptionHandler != null) {
|
||||||
|
try {
|
||||||
|
exceptionHandler.onException(cause);
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
|
||||||
|
ensureOpen();
|
||||||
|
long queryId = clientManager.getNextQueryId();
|
||||||
|
if (resultHandler != null) {
|
||||||
|
handlers.put(queryId, new Handler(resultHandler, exceptionHandler));
|
||||||
|
}
|
||||||
|
NativeClientAccess.send(clientId, queryId, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object execute(Function query) {
|
||||||
|
ensureOpen();
|
||||||
|
return NativeClientAccess.execute(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureOpen() {
|
||||||
|
if (isClosed) {
|
||||||
|
throw new IllegalStateException("The client is closed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/main/java/it/tdlight/common/InternalClientManager.java
Normal file
56
src/main/java/it/tdlight/common/InternalClientManager.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
import it.tdlight.jni.TdApi.Object;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
public class InternalClientManager implements AutoCloseable {
|
||||||
|
|
||||||
|
private static final AtomicReference<InternalClientManager> INSTANCE = new AtomicReference<>(null);
|
||||||
|
|
||||||
|
private final String implementationName;
|
||||||
|
private final ResponseReceiver responseReceiver = new ResponseReceiver(this::handleClientEvents);
|
||||||
|
private final Int2ObjectMap<ClientEventsHandler> clientEventsHandlerMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
private final AtomicLong currentQueryId = new AtomicLong();
|
||||||
|
|
||||||
|
private InternalClientManager(String implementationName) {
|
||||||
|
this.implementationName = implementationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InternalClientManager get(String implementationName) {
|
||||||
|
return INSTANCE.updateAndGet(val -> val == null ? new InternalClientManager(implementationName) : val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleClientEvents(int clientId, boolean isClosed, long[] clientEventIds, Object[] clientEvents) {
|
||||||
|
ClientEventsHandler handler = clientEventsHandlerMap.get(clientId);
|
||||||
|
|
||||||
|
if (handler != null) {
|
||||||
|
handler.handleEvents(isClosed, clientEventIds, clientEvents);
|
||||||
|
} else {
|
||||||
|
System.err.println("Unknown client id " + clientId + ", " + clientEvents.length + " events have been dropped!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isClosed) {
|
||||||
|
clientEventsHandlerMap.remove(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerClient(ClientEventsHandler client) {
|
||||||
|
this.clientEventsHandlerMap.put(client.getClientId(), client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImplementationName() {
|
||||||
|
return implementationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getNextQueryId() {
|
||||||
|
return currentQueryId.getAndUpdate(value -> (value >= Long.MAX_VALUE ? 0 : value) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws InterruptedException {
|
||||||
|
responseReceiver.close();
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/it/tdlight/common/MultiHandler.java
Normal file
19
src/main/java/it/tdlight/common/MultiHandler.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
public class MultiHandler {
|
||||||
|
private final UpdatesHandler updatesHandler;
|
||||||
|
private final ExceptionHandler exceptionHandler;
|
||||||
|
|
||||||
|
public MultiHandler(UpdatesHandler updatesHandler, ExceptionHandler exceptionHandler) {
|
||||||
|
this.updatesHandler = updatesHandler;
|
||||||
|
this.exceptionHandler = exceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdatesHandler getUpdatesHandler() {
|
||||||
|
return updatesHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExceptionHandler getExceptionHandler() {
|
||||||
|
return exceptionHandler;
|
||||||
|
}
|
||||||
|
}
|
24
src/main/java/it/tdlight/common/NativeClientAccess.java
Normal file
24
src/main/java/it/tdlight/common/NativeClientAccess.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
import it.tdlight.jni.NativeClient;
|
||||||
|
import it.tdlight.jni.TdApi;
|
||||||
|
import it.tdlight.jni.TdApi.Function;
|
||||||
|
|
||||||
|
class NativeClientAccess extends NativeClient {
|
||||||
|
|
||||||
|
public static int create() {
|
||||||
|
return NativeClientAccess.createNativeClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TdApi.Object execute(Function function) {
|
||||||
|
return NativeClientAccess.nativeClientExecute(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void send(int nativeClientId, long eventId, TdApi.Function function) {
|
||||||
|
NativeClientAccess.nativeClientSend(nativeClientId, eventId, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int receive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout) {
|
||||||
|
return NativeClientAccess.nativeClientReceive(clientIds, eventIds, events, timeout);
|
||||||
|
}
|
||||||
|
}
|
100
src/main/java/it/tdlight/common/ResponseReceiver.java
Normal file
100
src/main/java/it/tdlight/common/ResponseReceiver.java
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
import it.tdlight.jni.TdApi;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
public class ResponseReceiver extends Thread implements AutoCloseable {
|
||||||
|
|
||||||
|
private static final int MAX_EVENTS = 1000;
|
||||||
|
private static final int[] originalSortingSource = new int[MAX_EVENTS];
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < originalSortingSource.length; i++) {
|
||||||
|
originalSortingSource[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final EventsHandler eventsHandler;
|
||||||
|
private final int[] clientIds = new int[MAX_EVENTS];
|
||||||
|
private final long[] eventIds = new long[MAX_EVENTS];
|
||||||
|
private final TdApi.Object[] events = new TdApi.Object[MAX_EVENTS];
|
||||||
|
|
||||||
|
private final CountDownLatch closeWait = new CountDownLatch(1);
|
||||||
|
|
||||||
|
|
||||||
|
public ResponseReceiver(EventsHandler eventsHandler) {
|
||||||
|
super("TDLib thread");
|
||||||
|
this.eventsHandler = eventsHandler;
|
||||||
|
|
||||||
|
this.setDaemon(true);
|
||||||
|
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"UnnecessaryLocalVariable", "InfiniteLoopStatement"})
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int[] sortIndex;
|
||||||
|
try {
|
||||||
|
while(true) {
|
||||||
|
int resultsCount = NativeClientAccess.receive(clientIds, eventIds, events, 2.0 /*seconds*/);
|
||||||
|
|
||||||
|
if (resultsCount <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Generate a list of indices sorted by client id, from 0 to resultsCount
|
||||||
|
sortIndex = generateSortIndex(0, resultsCount, clientIds);
|
||||||
|
|
||||||
|
int lastClientId = clientIds[sortIndex[0]];
|
||||||
|
int lastClientIdEventsCount = 0;
|
||||||
|
boolean lastClientClosed = false;
|
||||||
|
|
||||||
|
for (int i = 0; i <= resultsCount; i++) {
|
||||||
|
if (i == resultsCount || (i != 0 && clientIds[sortIndex[i]] != lastClientId)) {
|
||||||
|
if (lastClientIdEventsCount > 0) {
|
||||||
|
int clientId = lastClientId;
|
||||||
|
long[] clientEventIds = new long[lastClientIdEventsCount];
|
||||||
|
TdApi.Object[] clientEvents = new TdApi.Object[lastClientIdEventsCount];
|
||||||
|
for (int j = 0; j < lastClientIdEventsCount; j++) {
|
||||||
|
clientEventIds[j] = eventIds[sortIndex[i - lastClientIdEventsCount + j]];
|
||||||
|
clientEvents[j] = events[sortIndex[i - lastClientIdEventsCount + j]];
|
||||||
|
|
||||||
|
if (clientEventIds[j] == 0 && clientEvents[j] instanceof TdApi.UpdateAuthorizationState) {
|
||||||
|
TdApi.AuthorizationState authorizationState = ((TdApi.UpdateAuthorizationState) clientEvents[j]).authorizationState;
|
||||||
|
if (authorizationState instanceof TdApi.AuthorizationStateClosed) {
|
||||||
|
lastClientClosed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventsHandler.handleClientEvents(clientId, lastClientClosed, clientEventIds, clientEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastClientId = clientIds[sortIndex[i]];
|
||||||
|
lastClientIdEventsCount = 0;
|
||||||
|
lastClientClosed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastClientIdEventsCount++;
|
||||||
|
}
|
||||||
|
Arrays.fill(events, null);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.closeWait.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
|
private int[] generateSortIndex(int from, int to, int[] data) {
|
||||||
|
int[] sortedIndices = Arrays.copyOfRange(originalSortingSource, from, to);
|
||||||
|
it.unimi.dsi.fastutil.Arrays.mergeSort(from, to, (o1, o2) -> {
|
||||||
|
return Integer.compare(data[sortedIndices[o1]], data[sortedIndices[o2]]);
|
||||||
|
}, new IntSwapper(sortedIndices));
|
||||||
|
return sortedIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws InterruptedException {
|
||||||
|
this.closeWait.await();
|
||||||
|
}
|
||||||
|
}
|
21
src/main/java/it/tdlight/common/TDLibException.java
Normal file
21
src/main/java/it/tdlight/common/TDLibException.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
import it.tdlight.jni.TdApi.Error;
|
||||||
|
|
||||||
|
public class TDLibException extends RuntimeException {
|
||||||
|
|
||||||
|
private final Error event;
|
||||||
|
|
||||||
|
public TDLibException(Error event) {
|
||||||
|
super(event.code + ": " + event.message);
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrorCode() {
|
||||||
|
return event.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorMessage() {
|
||||||
|
return event.message;
|
||||||
|
}
|
||||||
|
}
|
@ -1,74 +1,42 @@
|
|||||||
package it.tdlight.common;
|
package it.tdlight.common;
|
||||||
|
|
||||||
import java.io.IOException;
|
import it.tdlight.jni.TdApi;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface TelegramClient {
|
public interface TelegramClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends request to TDLib. May be called from any thread.
|
* Sends a request to the TDLib.
|
||||||
* @param request Request to TDLib.
|
|
||||||
*/
|
|
||||||
void send(Request request);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives incoming updates and request responses from TDLib. May be called from any thread, but shouldn't be called simultaneously from two different threads.
|
|
||||||
* @param timeout Maximum number of seconds allowed for this function to wait for new records.
|
|
||||||
* @param eventSize Maximum number of events allowed in list.
|
|
||||||
* @return An incoming update or request response list. The object returned in the response may be an empty list if the timeout expires.
|
|
||||||
*/
|
|
||||||
default List<Response> receive(double timeout, int eventSize) {
|
|
||||||
return receive(timeout, eventSize, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives incoming updates and/or request responses from TDLib. May be called from any thread, but shouldn't be called simultaneously from two different threads.
|
|
||||||
* @param timeout Maximum number of seconds allowed for this function to wait for new records.
|
|
||||||
* @param eventSize Maximum number of events allowed in list.
|
|
||||||
* @param receiveResponses True to receive responses.
|
|
||||||
* @param receiveUpdates True to receive updates from TDLib.
|
|
||||||
* @return An incoming update or request response list. The object returned in the response may be an empty list if the timeout expires.
|
|
||||||
*/
|
|
||||||
List<Response> receive(double timeout, int eventSize, boolean receiveResponses, boolean receiveUpdates);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives incoming updates and request responses from TDLib. May be called from any thread, but
|
|
||||||
* shouldn't be called simultaneously from two different threads.
|
|
||||||
*
|
*
|
||||||
* @param timeout Maximum number of seconds allowed for this function to wait for new records.
|
* @param query Object representing a query to the TDLib.
|
||||||
* @return An incoming update or request response. The object returned in the response may be a
|
* @param resultHandler Result handler with onResult method which will be called with result
|
||||||
* nullptr if the timeout expires.
|
* of the query or with TdApi.Error as parameter. If it is null, nothing
|
||||||
|
* will be called.
|
||||||
|
* @param exceptionHandler Exception handler with onException method which will be called on
|
||||||
|
* exception thrown from resultHandler. If it is null, then
|
||||||
|
* defaultExceptionHandler will be called.
|
||||||
|
* @throws NullPointerException if query is null.
|
||||||
*/
|
*/
|
||||||
default Response receive(double timeout) {
|
void send(TdApi.Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler);
|
||||||
return receive(timeout, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives incoming updates and request responses from TDLib. May be called from any thread, but
|
* Sends a request to the TDLib with an empty ExceptionHandler.
|
||||||
* shouldn't be called simultaneously from two different threads.
|
|
||||||
*
|
*
|
||||||
* @param timeout Maximum number of seconds allowed for this function to wait for new records.
|
* @param query Object representing a query to the TDLib.
|
||||||
* @param receiveResponses True to receive responses.
|
* @param resultHandler Result handler with onResult method which will be called with result
|
||||||
* @param receiveUpdates True to receive updates from TDLib.
|
* of the query or with TdApi.Error as parameter. If it is null, then
|
||||||
* @return An incoming update or request response. The object returned in the response may be a
|
* defaultExceptionHandler will be called.
|
||||||
* nullptr if the timeout expires.
|
* @throws NullPointerException if query is null.
|
||||||
*/
|
*/
|
||||||
Response receive(double timeout, boolean receiveResponses, boolean receiveUpdates);
|
default void send(TdApi.Function query, ResultHandler resultHandler) {
|
||||||
|
send(query, resultHandler, null);
|
||||||
/**
|
}
|
||||||
* Synchronously executes TDLib requests. Only a few requests can be executed synchronously. May be called from any thread.
|
|
||||||
* @param request Request to the TDLib.
|
/**
|
||||||
* @return The request response.
|
* Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously.
|
||||||
*/
|
*
|
||||||
Response execute(Request request);
|
* @param query Object representing a query to the TDLib.
|
||||||
|
* @return request result.
|
||||||
/**
|
* @throws NullPointerException if query is null.
|
||||||
* Destroys the client and TDLib instance.
|
*/
|
||||||
*/
|
TdApi.Object execute(TdApi.Function query);
|
||||||
void destroyClient();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the client and TDLib instance.
|
|
||||||
*/
|
|
||||||
void initializeClient() throws IOException;
|
|
||||||
}
|
}
|
||||||
|
17
src/main/java/it/tdlight/common/UpdatesHandler.java
Normal file
17
src/main/java/it/tdlight/common/UpdatesHandler.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package it.tdlight.common;
|
||||||
|
|
||||||
|
import it.tdlight.jni.TdApi.Update;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for handler for incoming updates from TDLib.
|
||||||
|
*/
|
||||||
|
public interface UpdatesHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called on incoming update from TDLib.
|
||||||
|
*
|
||||||
|
* @param object Updates of type TdApi.Update about new events.
|
||||||
|
*/
|
||||||
|
void onUpdates(List<Update> object);
|
||||||
|
}
|
@ -8,7 +8,5 @@ public class NativeClient {
|
|||||||
|
|
||||||
protected static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout);
|
protected static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout);
|
||||||
|
|
||||||
protected static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout, boolean include_responses, boolean include_updates);
|
|
||||||
|
|
||||||
protected static native TdApi.Object nativeClientExecute(TdApi.Function function);
|
protected static native TdApi.Object nativeClientExecute(TdApi.Function function);
|
||||||
}
|
}
|
||||||
|
@ -1,248 +1,14 @@
|
|||||||
package it.tdlight.tdlib;
|
package it.tdlight.tdlib;
|
||||||
|
|
||||||
import it.cavallium.concurrentlocks.ReentrantReadWriteUpdateLock;
|
import it.tdlight.common.CommonClient;
|
||||||
import it.tdlight.common.Init;
|
|
||||||
import it.tdlight.common.Request;
|
|
||||||
import it.tdlight.common.Response;
|
|
||||||
import it.tdlight.jni.NativeClient;
|
|
||||||
import it.tdlight.jni.TdApi;
|
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateClosed;
|
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateClosing;
|
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitTdlibParameters;
|
|
||||||
import it.tdlight.jni.TdApi.GetOption;
|
|
||||||
import it.tdlight.jni.TdApi.Object;
|
|
||||||
import it.tdlight.jni.TdApi.SetOption;
|
|
||||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
|
||||||
import it.tdlight.common.TelegramClient;
|
|
||||||
import it.tdlight.common.ClientState;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for interaction with TDLib.
|
* Interface for interaction with TDLib.
|
||||||
*/
|
*/
|
||||||
public class Client extends NativeClient implements TelegramClient {
|
public class Client extends CommonClient {
|
||||||
|
|
||||||
private ClientState state = ClientState.of(false, 0, false, false, false);
|
|
||||||
private final ReentrantReadWriteUpdateLock stateLock = new ReentrantReadWriteUpdateLock();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new TDLib client.
|
|
||||||
*/
|
|
||||||
public Client() {
|
|
||||||
try {
|
|
||||||
Init.start();
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
this.initializeClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(Request request) {
|
protected String getImplementationName() {
|
||||||
long clientId;
|
return "tdlib";
|
||||||
stateLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
requireInitialized();
|
|
||||||
requireReadyToSend(request.getFunction().getConstructor());
|
|
||||||
clientId = state.getClientId();
|
|
||||||
} finally {
|
|
||||||
stateLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
nativeClientSend(clientId, request.getId(), request.getFunction());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Response> receive(double timeout, int eventsSize, boolean receiveResponses, boolean receiveUpdates) {
|
|
||||||
long clientId;
|
|
||||||
stateLock.updateLock().lock();
|
|
||||||
try {
|
|
||||||
if (!state.isInitialized()) {
|
|
||||||
sleep(timeout);
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
requireInitialized();
|
|
||||||
if (!state.isReadyToReceive()) {
|
|
||||||
sleep(timeout);
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
requireReadyToReceive();
|
|
||||||
clientId = state.getClientId();
|
|
||||||
return Arrays.asList(this.internalReceive(clientId, timeout, eventsSize, receiveResponses, receiveUpdates));
|
|
||||||
} finally {
|
|
||||||
stateLock.updateLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sleep(double timeout) {
|
|
||||||
long nanos = (long) (timeout * 1000000000d);
|
|
||||||
int nanosPart = (int) (nanos % 1000000L);
|
|
||||||
long millis = Duration.ofNanos(nanos - nanosPart).toMillis();
|
|
||||||
try {
|
|
||||||
Thread.sleep(millis, nanosPart);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response receive(double timeout, boolean receiveResponses, boolean receiveUpdates) {
|
|
||||||
long clientId;
|
|
||||||
stateLock.updateLock().lock();
|
|
||||||
try {
|
|
||||||
if (!state.isInitialized()) {
|
|
||||||
sleep(timeout);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
requireInitialized();
|
|
||||||
if (!state.isReadyToReceive()) {
|
|
||||||
sleep(timeout);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
requireReadyToReceive();
|
|
||||||
clientId = state.getClientId();
|
|
||||||
|
|
||||||
Response[] responses = this.internalReceive(clientId, timeout, 1, receiveResponses, receiveUpdates);
|
|
||||||
|
|
||||||
if (responses.length > 0) {
|
|
||||||
return responses[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
stateLock.updateLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Response[] internalReceive(long clientId, double timeout, int eventsSize, boolean receiveResponses, boolean receiveUpdates) {
|
|
||||||
long[] eventIds = new long[eventsSize];
|
|
||||||
TdApi.Object[] events = new TdApi.Object[eventsSize];
|
|
||||||
|
|
||||||
if (!(receiveResponses && receiveUpdates)) {
|
|
||||||
throw new IllegalArgumentException("The variables receiveResponses and receiveUpdates must be both true, because you are using the original TDLib!");
|
|
||||||
}
|
|
||||||
|
|
||||||
int resultSize = nativeClientReceive(clientId, eventIds, events, timeout);
|
|
||||||
|
|
||||||
Response[] responses = new Response[resultSize];
|
|
||||||
|
|
||||||
for (int i = 0; i < resultSize; i++) {
|
|
||||||
responses[i] = new Response(eventIds[i], events[i]);
|
|
||||||
if (eventIds[i] == 0) {
|
|
||||||
handleStateEvent(events[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return responses;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleStateEvent(Object event) {
|
|
||||||
if (event == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.getConstructor() != UpdateAuthorizationState.CONSTRUCTOR) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateAuthorizationState updateAuthorizationState = (UpdateAuthorizationState) event;
|
|
||||||
|
|
||||||
switch (updateAuthorizationState.authorizationState.getConstructor()) {
|
|
||||||
case AuthorizationStateWaitTdlibParameters.CONSTRUCTOR:
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
state.setReadyToSend(true);
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AuthorizationStateClosing.CONSTRUCTOR:
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
state.setReadyToSend(false);
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AuthorizationStateClosed.CONSTRUCTOR:
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
state.setReadyToSend(false).setReadyToReceive(false);
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response execute(Request request) {
|
|
||||||
stateLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
requireInitialized();
|
|
||||||
requireReadyToSend(request.getFunction().getConstructor());
|
|
||||||
} finally {
|
|
||||||
stateLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
Object object = nativeClientExecute(request.getFunction());
|
|
||||||
return new Response(0, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroyClient() {
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
if (state.isInitialized() && state.hasClientId()) {
|
|
||||||
if (state.isReadyToSend() || state.isReadyToReceive()) {
|
|
||||||
throw new IllegalStateException("You need to close the Client before destroying it!");
|
|
||||||
}
|
|
||||||
destroyNativeClient(this.state.getClientId());
|
|
||||||
state = ClientState.of(false, 0, false, false, false);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initializeClient() {
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
if (!state.isInitialized() && !state.hasClientId()) {
|
|
||||||
long clientId = createNativeClient();
|
|
||||||
state = ClientState.of(true, clientId, true, true, false);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requireInitialized() {
|
|
||||||
if (!state.isInitialized() || !state.hasClientId()) {
|
|
||||||
throw new IllegalStateException("Client not initialized");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requireReadyToSend(int constructor) {
|
|
||||||
if (!state.isReadyToSend()) {
|
|
||||||
switch (constructor) {
|
|
||||||
case SetOption.CONSTRUCTOR:
|
|
||||||
case GetOption.CONSTRUCTOR:
|
|
||||||
case TdApi.SetTdlibParameters.CONSTRUCTOR:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("Client not ready to send");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requireReadyToReceive() {
|
|
||||||
if (!state.isReadyToReceive()) {
|
|
||||||
throw new IllegalStateException("Client not ready to receive");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,287 +1,14 @@
|
|||||||
package it.tdlight.tdlight;
|
package it.tdlight.tdlight;
|
||||||
|
|
||||||
import it.tdlight.common.ClientState;
|
import it.tdlight.common.CommonClient;
|
||||||
import it.tdlight.common.Init;
|
|
||||||
import it.tdlight.common.Request;
|
|
||||||
import it.tdlight.common.Response;
|
|
||||||
import it.tdlight.common.TelegramClient;
|
|
||||||
import it.tdlight.jni.NativeClient;
|
|
||||||
import it.tdlight.jni.TdApi;
|
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateClosed;
|
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateClosing;
|
|
||||||
import it.tdlight.jni.TdApi.AuthorizationStateWaitTdlibParameters;
|
|
||||||
import it.tdlight.jni.TdApi.GetOption;
|
|
||||||
import it.tdlight.jni.TdApi.Object;
|
|
||||||
import it.tdlight.jni.TdApi.SetOption;
|
|
||||||
import it.tdlight.jni.TdApi.UpdateAuthorizationState;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for interaction with TDLib.
|
* Interface for interaction with TDLight.
|
||||||
*/
|
*/
|
||||||
public class Client extends NativeClient implements TelegramClient {
|
public class Client extends CommonClient {
|
||||||
|
|
||||||
private ClientState state = ClientState.of(false, 0, false, false, false);
|
|
||||||
private final ReentrantReadWriteLock stateLock = new ReentrantReadWriteLock();
|
|
||||||
private final ReentrantReadWriteLock updatesLock = new ReentrantReadWriteLock();
|
|
||||||
private final ReentrantReadWriteLock responsesLock = new ReentrantReadWriteLock();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new TDLib client.
|
|
||||||
*/
|
|
||||||
public Client() {
|
|
||||||
try {
|
|
||||||
Init.start();
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
this.initializeClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(Request request) {
|
protected String getImplementationName() {
|
||||||
long clientId;
|
return "tdlight";
|
||||||
stateLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
requireInitialized();
|
|
||||||
requireReadyToSend(request.getFunction().getConstructor());
|
|
||||||
clientId = state.getClientId();
|
|
||||||
} finally {
|
|
||||||
stateLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
nativeClientSend(clientId, request.getId(), request.getFunction());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Response> receive(double timeout, int eventsSize, boolean receiveResponses, boolean receiveUpdates) {
|
|
||||||
if (receiveResponses) responsesLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
if (receiveUpdates) updatesLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
long clientId;
|
|
||||||
stateLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
if (!state.isInitialized()) {
|
|
||||||
sleep(timeout);
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
requireInitialized();
|
|
||||||
if (!state.isReadyToReceive()) {
|
|
||||||
sleep(timeout);
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
requireReadyToReceive();
|
|
||||||
clientId = state.getClientId();
|
|
||||||
} finally {
|
|
||||||
stateLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Arrays.asList(this.internalReceive(clientId, timeout, eventsSize, receiveResponses, receiveUpdates));
|
|
||||||
} finally {
|
|
||||||
if (receiveUpdates) updatesLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (receiveResponses) responsesLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sleep(double timeout) {
|
|
||||||
long nanos = (long) (timeout * 1000000000d);
|
|
||||||
int nanosPart = (int) (nanos % 1000000L);
|
|
||||||
long millis = Duration.ofNanos(nanos - nanosPart).toMillis();
|
|
||||||
try {
|
|
||||||
Thread.sleep(millis, nanosPart);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response receive(double timeout, boolean receiveResponses, boolean receiveUpdates) {
|
|
||||||
responsesLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
updatesLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
long clientId;
|
|
||||||
stateLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
if (!state.isInitialized()) {
|
|
||||||
sleep(timeout);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
requireInitialized();
|
|
||||||
if (!state.isReadyToReceive()) {
|
|
||||||
sleep(timeout);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
requireReadyToReceive();
|
|
||||||
clientId = state.getClientId();
|
|
||||||
} finally {
|
|
||||||
stateLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
Response[] responses = this.internalReceive(clientId, timeout, 1, receiveResponses, receiveUpdates);
|
|
||||||
|
|
||||||
if (responses.length > 0) {
|
|
||||||
return responses[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
updatesLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
responsesLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Response[] internalReceive(long clientId, double timeout, int eventsSize, boolean receiveResponses, boolean receiveUpdates) {
|
|
||||||
long[] eventIds = new long[eventsSize];
|
|
||||||
TdApi.Object[] events = new TdApi.Object[eventsSize];
|
|
||||||
|
|
||||||
int resultSize = nativeClientReceive(clientId, eventIds, events, timeout, receiveResponses, receiveUpdates);
|
|
||||||
|
|
||||||
Response[] responses = new Response[resultSize];
|
|
||||||
|
|
||||||
for (int i = 0; i < resultSize; i++) {
|
|
||||||
responses[i] = new Response(eventIds[i], events[i]);
|
|
||||||
if (eventIds[i] == 0) {
|
|
||||||
handleStateEvent(events[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return responses;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleStateEvent(Object event) {
|
|
||||||
if (event == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.getConstructor() != UpdateAuthorizationState.CONSTRUCTOR) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateAuthorizationState updateAuthorizationState = (UpdateAuthorizationState) event;
|
|
||||||
|
|
||||||
switch (updateAuthorizationState.authorizationState.getConstructor()) {
|
|
||||||
case AuthorizationStateWaitTdlibParameters.CONSTRUCTOR:
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
state.setReadyToSend(true);
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AuthorizationStateClosing.CONSTRUCTOR:
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
state.setReadyToSend(false);
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AuthorizationStateClosed.CONSTRUCTOR:
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
state.setReadyToSend(false).setReadyToReceive(false);
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response execute(Request request) {
|
|
||||||
stateLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
requireInitialized();
|
|
||||||
requireReadyToSend(request.getFunction().getConstructor());
|
|
||||||
} finally {
|
|
||||||
stateLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
Object object = nativeClientExecute(request.getFunction());
|
|
||||||
return new Response(0, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroyClient() {
|
|
||||||
responsesLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
updatesLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
if (state.isInitialized() && state.hasClientId()) {
|
|
||||||
if (state.isReadyToSend() || state.isReadyToReceive()) {
|
|
||||||
throw new IllegalStateException("You need to close the Client before destroying it!");
|
|
||||||
}
|
|
||||||
destroyNativeClient(this.state.getClientId());
|
|
||||||
state = ClientState.of(false, 0, false, false, false);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
updatesLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
responsesLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initializeClient() {
|
|
||||||
responsesLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
updatesLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
stateLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
if (!state.isInitialized() && !state.hasClientId()) {
|
|
||||||
long clientId = createNativeClient();
|
|
||||||
state = ClientState.of(true, clientId, true, true, false);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
stateLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
updatesLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
responsesLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requireInitialized() {
|
|
||||||
if (!state.isInitialized() || !state.hasClientId()) {
|
|
||||||
throw new IllegalStateException("Client not initialized");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requireReadyToSend(int constructor) {
|
|
||||||
if (!state.isReadyToSend()) {
|
|
||||||
switch (constructor) {
|
|
||||||
case SetOption.CONSTRUCTOR:
|
|
||||||
case GetOption.CONSTRUCTOR:
|
|
||||||
case TdApi.SetTdlibParameters.CONSTRUCTOR:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("Client not ready to send");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requireReadyToReceive() {
|
|
||||||
if (!state.isReadyToReceive()) {
|
|
||||||
throw new IllegalStateException("Client not ready to receive");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,28 +42,33 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.tdlight</groupId>
|
<groupId>it.tdlight</groupId>
|
||||||
<artifactId>tdlib-natives-linux-amd64</artifactId>
|
<artifactId>tdlib-natives-linux-amd64</artifactId>
|
||||||
<version>3.0.84</version>
|
<version>3.0.92</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.tdlight</groupId>
|
<groupId>it.tdlight</groupId>
|
||||||
<artifactId>tdlib-natives-linux-aarch64</artifactId>
|
<artifactId>tdlib-natives-linux-aarch64</artifactId>
|
||||||
<version>3.0.84</version>
|
<version>3.0.92</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.tdlight</groupId>
|
<groupId>it.tdlight</groupId>
|
||||||
<artifactId>tdlib-natives-windows-amd64</artifactId>
|
<artifactId>tdlib-natives-windows-amd64</artifactId>
|
||||||
<version>3.0.84</version>
|
<version>3.0.92</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.tdlight</groupId>
|
<groupId>it.tdlight</groupId>
|
||||||
<artifactId>tdlib-natives-osx-amd64</artifactId>
|
<artifactId>tdlib-natives-osx-amd64</artifactId>
|
||||||
<version>3.0.84</version>
|
<version>3.0.92</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.cavallium</groupId>
|
<groupId>it.cavallium</groupId>
|
||||||
<artifactId>concurrent-locks</artifactId>
|
<artifactId>concurrent-locks</artifactId>
|
||||||
<version>1.0.5</version>
|
<version>1.0.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>it.unimi.dsi</groupId>
|
||||||
|
<artifactId>fastutil</artifactId>
|
||||||
|
<version>8.3.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<sourceDirectory>../src/main/java</sourceDirectory>
|
<sourceDirectory>../src/main/java</sourceDirectory>
|
||||||
|
@ -42,28 +42,33 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.tdlight</groupId>
|
<groupId>it.tdlight</groupId>
|
||||||
<artifactId>tdlight-natives-linux-amd64</artifactId>
|
<artifactId>tdlight-natives-linux-amd64</artifactId>
|
||||||
<version>3.0.84</version>
|
<version>3.0.92</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.tdlight</groupId>
|
<groupId>it.tdlight</groupId>
|
||||||
<artifactId>tdlight-natives-linux-aarch64</artifactId>
|
<artifactId>tdlight-natives-linux-aarch64</artifactId>
|
||||||
<version>3.0.84</version>
|
<version>3.0.92</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.tdlight</groupId>
|
<groupId>it.tdlight</groupId>
|
||||||
<artifactId>tdlight-natives-windows-amd64</artifactId>
|
<artifactId>tdlight-natives-windows-amd64</artifactId>
|
||||||
<version>3.0.84</version>
|
<version>3.0.92</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.tdlight</groupId>
|
<groupId>it.tdlight</groupId>
|
||||||
<artifactId>tdlight-natives-osx-amd64</artifactId>
|
<artifactId>tdlight-natives-osx-amd64</artifactId>
|
||||||
<version>3.0.84</version>
|
<version>3.0.92</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>it.cavallium</groupId>
|
<groupId>it.cavallium</groupId>
|
||||||
<artifactId>concurrent-locks</artifactId>
|
<artifactId>concurrent-locks</artifactId>
|
||||||
<version>1.0.5</version>
|
<version>1.0.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>it.unimi.dsi</groupId>
|
||||||
|
<artifactId>fastutil</artifactId>
|
||||||
|
<version>8.3.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<sourceDirectory>../src/main/java</sourceDirectory>
|
<sourceDirectory>../src/main/java</sourceDirectory>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user