Use generics to check tdlib functions return types

This commit is contained in:
Andrea Cavalli 2021-10-20 23:51:06 +02:00
parent 6deb99f517
commit 646330ae19
19 changed files with 118 additions and 101 deletions

View File

@ -37,7 +37,7 @@ final class AuthorizationStateWaitCodeHandler implements GenericUpdateHandler<Up
authorizationState.codeInfo.type authorizationState.codeInfo.type
); );
String code = clientInteraction.onParameterRequest(InputParameter.ASK_CODE, parameterInfo); String code = clientInteraction.onParameterRequest(InputParameter.ASK_CODE, parameterInfo);
Function response = new CheckAuthenticationCode(code); CheckAuthenticationCode response = new CheckAuthenticationCode(code);
client.send(response, ok -> { client.send(response, ok -> {
if (ok.getConstructor() == Error.CONSTRUCTOR) { if (ok.getConstructor() == Error.CONSTRUCTOR) {
throw new TelegramError((Error) ok); throw new TelegramError((Error) ok);

View File

@ -36,7 +36,7 @@ final class AuthorizationStateWaitPasswordHandler implements GenericUpdateHandle
authorizationState.recoveryEmailAddressPattern authorizationState.recoveryEmailAddressPattern
); );
String password = clientInteraction.onParameterRequest(InputParameter.ASK_PASSWORD, parameterInfo); String password = clientInteraction.onParameterRequest(InputParameter.ASK_PASSWORD, parameterInfo);
Function response = new CheckAuthenticationPassword(password); CheckAuthenticationPassword response = new CheckAuthenticationPassword(password);
client.send(response, ok -> { client.send(response, ok -> {
if (ok.getConstructor() == Error.CONSTRUCTOR) { if (ok.getConstructor() == Error.CONSTRUCTOR) {
throw new TelegramError((Error) ok); throw new TelegramError((Error) ok);

View File

@ -71,14 +71,19 @@ final class ConsoleInteractiveAuthenticationData implements AuthenticationData {
.askParameter("login", "Do you want to login using a bot [token], a [phone] number, or a [qr] code? [token/phone/qr]") .askParameter("login", "Do you want to login using a bot [token], a [phone] number, or a [qr] code? [token/phone/qr]")
.trim() .trim()
.toLowerCase(Locale.ROOT); .toLowerCase(Locale.ROOT);
if ("phone".equals(choice)) { switch (choice) {
mode = "PHONE"; case "phone":
} else if ("token".equals(choice)) { mode = "PHONE";
mode = "TOKEN"; break;
} else if ("qr".equals(choice)) { case "token":
mode = "QR"; mode = "TOKEN";
} else { break;
mode = null; case "qr":
mode = "QR";
break;
default:
mode = null;
break;
} }
} while (mode == null); } while (mode == null);

View File

@ -49,7 +49,8 @@ public final class SimpleTelegramClient implements Authenticable {
private AuthenticationData authenticationData; private AuthenticationData authenticationData;
private final Map<String, Set<CommandHandler>> commandHandlers = new ConcurrentHashMap<>(); private final Map<String, Set<CommandHandler>> commandHandlers = new ConcurrentHashMap<>();
private final Set<ResultHandler> updateHandlers = new ConcurrentHashMap<ResultHandler, Object>() private final Set<ResultHandler<TdApi.Update>> updateHandlers
= new ConcurrentHashMap<ResultHandler<TdApi.Update>, Object>()
.keySet(new Object()); .keySet(new Object());
private final Set<ExceptionHandler> updateExceptionHandlers = new ConcurrentHashMap<ExceptionHandler, Object>() private final Set<ExceptionHandler> updateExceptionHandlers = new ConcurrentHashMap<ExceptionHandler, Object>()
.keySet(new Object()); .keySet(new Object());
@ -87,7 +88,7 @@ public final class SimpleTelegramClient implements Authenticable {
private void handleUpdate(TdApi.Object update) { private void handleUpdate(TdApi.Object update) {
boolean handled = false; boolean handled = false;
for (ResultHandler updateHandler : updateHandlers) { for (ResultHandler<TdApi.Update> updateHandler : updateHandlers) {
updateHandler.onResult(update); updateHandler.onResult(update);
handled = true; handled = true;
} }
@ -208,7 +209,7 @@ public final class SimpleTelegramClient implements Authenticable {
/** /**
* Send a function and get the result * Send a function and get the result
*/ */
public <T extends TdApi.Object> void send(TdApi.Function function, GenericResultHandler<T> resultHandler) { public <R extends TdApi.Object> void send(TdApi.Function<R> function, GenericResultHandler<R> resultHandler) {
client.send(function, result -> resultHandler.onResult(Result.of(result)), this::handleResultHandlingException); client.send(function, result -> resultHandler.onResult(Result.of(result)), this::handleResultHandlingException);
} }
@ -217,7 +218,7 @@ public final class SimpleTelegramClient implements Authenticable {
* <strong>Please note that only some functions can be executed using this method.</strong> * <strong>Please note that only some functions can be executed using this method.</strong>
* If you want to execute a function please use {@link #send(Function, GenericResultHandler)}! * If you want to execute a function please use {@link #send(Function, GenericResultHandler)}!
*/ */
public <T extends TdApi.Object> Result<T> execute(TdApi.Function function) { public <R extends TdApi.Object> Result<R> execute(TdApi.Function<R> function) {
return Result.of(client.execute(function)); return Result.of(client.execute(function));
} }

View File

@ -35,12 +35,6 @@ public final class Init {
*/ */
public synchronized static void start() throws CantLoadLibrary { public synchronized static void start() throws CantLoadLibrary {
if (!started) { if (!started) {
Os os = LoadLibrary.getOs();
if (os == Os.WINDOWS) {
// Since 3.0.0, libraries for windows are statically compiled into tdjni.dll
}
LoadLibrary.load("tdjni"); LoadLibrary.load("tdjni");
ConstructorDetector.init(); ConstructorDetector.init();
started = true; started = true;

View File

@ -4,7 +4,6 @@ import it.tdlight.jni.TdApi;
import java.time.Duration; import java.time.Duration;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
@SuppressWarnings("ReactiveStreamsPublisherImplementation")
public interface ReactiveTelegramClient { public interface ReactiveTelegramClient {
/** /**
@ -20,7 +19,7 @@ public interface ReactiveTelegramClient {
* @return a publisher that will emit exactly one item, or an error * @return a publisher that will emit exactly one item, or an error
* @throws NullPointerException if query is null. * @throws NullPointerException if query is null.
*/ */
Publisher<TdApi.Object> send(TdApi.Function query, Duration timeout); <R extends TdApi.Object> Publisher<TdApi.Object> send(TdApi.Function<R> query, Duration timeout);
/** /**
* Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously. * Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously.
@ -29,7 +28,7 @@ public interface ReactiveTelegramClient {
* @return request result or {@link TdApi.Error}. * @return request result or {@link TdApi.Error}.
* @throws NullPointerException if query is null. * @throws NullPointerException if query is null.
*/ */
TdApi.Object execute(TdApi.Function query); <R extends TdApi.Object> TdApi.Object execute(TdApi.Function<R> query);
void setListener(SignalListener listener); void setListener(SignalListener listener);

View File

@ -1,16 +1,18 @@
package it.tdlight.common; package it.tdlight.common;
import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Object; import it.tdlight.jni.TdApi.Object;
/** /**
* Interface for handler for results of queries to TDLib and incoming updates from TDLib. * Interface for handler for results of queries to TDLib and incoming updates from TDLib.
*/ */
public interface ResultHandler { @SuppressWarnings("unused")
public interface ResultHandler<R extends TdApi.Object> {
/** /**
* Callback called on result of query to TDLib or incoming update from TDLib. * Callback called on result of query to TDLib or incoming update from TDLib.
* *
* @param object Result of query or update of type TdApi.Update about new events. * @param object Result of type r, error of type TdApi.Error, or update of type TdApi.Update.
*/ */
void onResult(Object object); void onResult(TdApi.Object object);
} }

View File

@ -22,7 +22,7 @@ public interface TelegramClient {
* @param updateExceptionHandler Handler in which the errors from updates are received * @param updateExceptionHandler Handler in which the errors from updates are received
* @param defaultExceptionHandler Handler that receives exceptions triggered in a handler * @param defaultExceptionHandler Handler that receives exceptions triggered in a handler
*/ */
default void initialize(ResultHandler updateHandler, default void initialize(ResultHandler<TdApi.Update> updateHandler,
ExceptionHandler updateExceptionHandler, ExceptionHandler updateExceptionHandler,
ExceptionHandler defaultExceptionHandler) { ExceptionHandler defaultExceptionHandler) {
this.initialize((UpdatesHandler) updates -> updates.forEach(updateHandler::onResult), this.initialize((UpdatesHandler) updates -> updates.forEach(updateHandler::onResult),
@ -41,7 +41,8 @@ public interface TelegramClient {
* resultHandler. If it is null, then defaultExceptionHandler will be called. * resultHandler. If it is null, then defaultExceptionHandler will be called.
* @throws NullPointerException if query is null. * @throws NullPointerException if query is null.
*/ */
void send(TdApi.Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler); <R extends TdApi.Object> void send(TdApi.Function<R> query, ResultHandler<R> resultHandler,
ExceptionHandler exceptionHandler);
/** /**
* Sends a request to the TDLib with an empty ExceptionHandler. * Sends a request to the TDLib with an empty ExceptionHandler.
@ -51,7 +52,7 @@ public interface TelegramClient {
* TdApi.Error as parameter. If it is null, then defaultExceptionHandler will be called. * TdApi.Error as parameter. If it is null, then defaultExceptionHandler will be called.
* @throws NullPointerException if query is null. * @throws NullPointerException if query is null.
*/ */
default void send(TdApi.Function query, ResultHandler resultHandler) { default <R extends TdApi.Object> void send(TdApi.Function<R> query, ResultHandler<R> resultHandler) {
send(query, resultHandler, null); send(query, resultHandler, null);
} }
@ -62,5 +63,5 @@ public interface TelegramClient {
* @return request result or {@link TdApi.Error}. * @return request result or {@link TdApi.Error}.
* @throws NullPointerException if query is null. * @throws NullPointerException if query is null.
*/ */
TdApi.Object execute(TdApi.Function query); <R extends TdApi.Object> TdApi.Object execute(TdApi.Function<R> query);
} }

View File

@ -2,17 +2,18 @@ package it.tdlight.common.internal;
import it.tdlight.common.ExceptionHandler; import it.tdlight.common.ExceptionHandler;
import it.tdlight.common.ResultHandler; import it.tdlight.common.ResultHandler;
import it.tdlight.jni.TdApi;
public final class Handler { public final class Handler<R extends TdApi.Object> {
private final ResultHandler resultHandler; private final ResultHandler<R> resultHandler;
private final ExceptionHandler exceptionHandler; private final ExceptionHandler exceptionHandler;
public Handler(ResultHandler resultHandler, ExceptionHandler exceptionHandler) { public Handler(ResultHandler<R> resultHandler, ExceptionHandler exceptionHandler) {
this.resultHandler = resultHandler; this.resultHandler = resultHandler;
this.exceptionHandler = exceptionHandler; this.exceptionHandler = exceptionHandler;
} }
public ResultHandler getResultHandler() { public ResultHandler<R> getResultHandler() {
return resultHandler; return resultHandler;
} }

View File

@ -8,7 +8,6 @@ import it.tdlight.common.UpdatesHandler;
import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi;
import it.tdlight.jni.TdApi.Error; import it.tdlight.jni.TdApi.Error;
import it.tdlight.jni.TdApi.Function; import it.tdlight.jni.TdApi.Function;
import it.tdlight.jni.TdApi.Object;
import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.Objects; import java.util.Objects;
@ -24,13 +23,13 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
private static final Marker TG_MARKER = MarkerFactory.getMarker("TG"); private static final Marker TG_MARKER = MarkerFactory.getMarker("TG");
private static final Logger logger = LoggerFactory.getLogger(TelegramClient.class); private static final Logger logger = LoggerFactory.getLogger(TelegramClient.class);
private final ConcurrentHashMap<Long, Handler> handlers = new ConcurrentHashMap<Long, Handler>(); private final ConcurrentHashMap<Long, Handler<?>> handlers = new ConcurrentHashMap<>();
private final Thread shutdownHook = new Thread(this::onJVMShutdown); private final Thread shutdownHook = new Thread(this::onJVMShutdown);
private volatile Integer clientId = null; private volatile Integer clientId = null;
private final InternalClientManager clientManager; private final InternalClientManager clientManager;
private Handler updateHandler; private Handler<TdApi.Update> updateHandler;
private MultiHandler updatesHandler; private MultiHandler updatesHandler;
private ExceptionHandler defaultExceptionHandler; private ExceptionHandler defaultExceptionHandler;
@ -47,10 +46,10 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
} }
@Override @Override
public void handleEvents(boolean isClosed, long[] eventIds, Object[] events) { public void handleEvents(boolean isClosed, long[] eventIds, TdApi.Object[] events) {
if (updatesHandler != null) { if (updatesHandler != null) {
LongArrayList idsToFilter = new LongArrayList(eventIds); LongArrayList idsToFilter = new LongArrayList(eventIds);
ObjectArrayList<Object> eventsToFilter = new ObjectArrayList<>(events); ObjectArrayList<TdApi.Object> eventsToFilter = new ObjectArrayList<>(events);
for (int i = eventIds.length - 1; i >= 0; i--) { for (int i = eventIds.length - 1; i >= 0; i--) {
if (eventIds[i] != 0) { if (eventIds[i] != 0) {
@ -58,9 +57,9 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
eventsToFilter.remove(i); eventsToFilter.remove(i);
long eventId = eventIds[i]; long eventId = eventIds[i];
Object event = events[i]; TdApi.Object event = events[i];
Handler handler = handlers.remove(eventId); Handler<?> handler = handlers.remove(eventId);
handleResponse(eventId, event, handler); handleResponse(eventId, event, handler);
} }
} }
@ -90,9 +89,7 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
} catch (IllegalStateException ignored) { } catch (IllegalStateException ignored) {
logger.trace(TG_MARKER, "Can't remove shutdown hook because the JVM is already shutting down"); logger.trace(TG_MARKER, "Can't remove shutdown hook because the JVM is already shutting down");
} }
handlers.forEach((eventId, handler) -> { handlers.forEach((eventId, handler) -> handleResponse(eventId, new Error(500, "Instance closed"), handler));
handleResponse(eventId, new Error(500, "Instance closed"), handler);
});
handlers.clear(); handlers.clear();
logger.info(TG_MARKER, "Client closed {}", clientId); logger.info(TG_MARKER, "Client closed {}", clientId);
} }
@ -100,7 +97,7 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
/** /**
* Handles only a response (not an update!) * Handles only a response (not an update!)
*/ */
private void handleResponse(long eventId, Object event, Handler handler) { private void handleResponse(long eventId, TdApi.Object event, Handler<?> handler) {
if (handler != null) { if (handler != null) {
try { try {
handler.getResultHandler().onResult(event); handler.getResultHandler().onResult(event);
@ -115,10 +112,10 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
/** /**
* Handles a response or an update * Handles a response or an update
*/ */
private void handleEvent(long eventId, Object event) { private void handleEvent(long eventId, TdApi.Object event) {
logger.trace(TG_MARKER, "Received response {}: {}", eventId, event); logger.trace(TG_MARKER, "Received response {}: {}", eventId, event);
if (updatesHandler != null || updateHandler == null) throw new IllegalStateException(); if (updatesHandler != null || updateHandler == null) throw new IllegalStateException();
Handler handler = eventId == 0 ? updateHandler : handlers.remove(eventId); Handler<?> handler = eventId == 0 ? updateHandler : handlers.remove(eventId);
handleResponse(eventId, event, handler); handleResponse(eventId, event, handler);
} }
@ -144,10 +141,10 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
} }
@Override @Override
public void initialize(ResultHandler updateHandler, public void initialize(ResultHandler<TdApi.Update> updateHandler,
ExceptionHandler updateExceptionHandler, ExceptionHandler updateExceptionHandler,
ExceptionHandler defaultExceptionHandler) { ExceptionHandler defaultExceptionHandler) {
this.updateHandler = new Handler(updateHandler, updateExceptionHandler); this.updateHandler = new Handler<>(updateHandler, updateExceptionHandler);
this.updatesHandler = null; this.updatesHandler = null;
this.defaultExceptionHandler = defaultExceptionHandler; this.defaultExceptionHandler = defaultExceptionHandler;
createAndRegisterClient(); createAndRegisterClient();
@ -164,25 +161,27 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
} }
@Override @Override
public void send(Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { public <R extends TdApi.Object> void send(Function<R> query, ResultHandler<R> resultHandler,
ExceptionHandler exceptionHandler) {
logger.trace(TG_MARKER, "Trying to send {}", query); logger.trace(TG_MARKER, "Trying to send {}", query);
if (isClosedAndMaybeThrow(query)) { if (isClosedAndMaybeThrow(query)) {
resultHandler.onResult(new TdApi.Ok()); resultHandler.onResult(new TdApi.Ok());
} }
if (clientId == null) { if (clientId == null) {
ExceptionHandler handler = exceptionHandler == null ? defaultExceptionHandler : exceptionHandler; ExceptionHandler handler = exceptionHandler == null ? defaultExceptionHandler : exceptionHandler;
handler.onException(new IllegalStateException("Can't send a request to TDLib before calling \"initialize\" function!")); handler.onException(new IllegalStateException(
"Can't send a request to TDLib before calling \"initialize\" function!"));
return; return;
} }
long queryId = clientManager.getNextQueryId(); long queryId = clientManager.getNextQueryId();
if (resultHandler != null) { if (resultHandler != null) {
handlers.put(queryId, new Handler(resultHandler, exceptionHandler)); handlers.put(queryId, new Handler<>(resultHandler, exceptionHandler));
} }
NativeClientAccess.send(clientId, queryId, query); NativeClientAccess.send(clientId, queryId, query);
} }
@Override @Override
public Object execute(Function query) { public <R extends TdApi.Object> TdApi.Object execute(Function<R> query) {
logger.trace(TG_MARKER, "Trying to execute {}", query); logger.trace(TG_MARKER, "Trying to execute {}", query);
if (isClosedAndMaybeThrow(query)) { if (isClosedAndMaybeThrow(query)) {
return new TdApi.Ok(); return new TdApi.Ok();
@ -206,7 +205,7 @@ public final class InternalClient implements ClientEventsHandler, TelegramClient
* @param function function used to check if the check will be enforced or not. Can be null * @param function function used to check if the check will be enforced or not. Can be null
* @return true if closed * @return true if closed
*/ */
private boolean isClosedAndMaybeThrow(Function function) { private boolean isClosedAndMaybeThrow(Function<?> function) {
boolean closed = isClosed.get(); boolean closed = isClosed.get();
if (closed) { if (closed) {
if (function != null && function.getConstructor() == TdApi.Close.CONSTRUCTOR) { if (function != null && function.getConstructor() == TdApi.Close.CONSTRUCTOR) {

View File

@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription; import org.reactivestreams.Subscription;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -30,13 +31,13 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
private static final Marker TG_MARKER = MarkerFactory.getMarker("TG"); private static final Marker TG_MARKER = MarkerFactory.getMarker("TG");
private static final Logger logger = LoggerFactory.getLogger(InternalReactiveClient.class); private static final Logger logger = LoggerFactory.getLogger(InternalReactiveClient.class);
private static final Handler EMPTY_HANDLER = new Handler(r -> {}, ex -> {}); private static final Handler<?> EMPTY_HANDLER = new Handler<>(r -> {}, ex -> {});
private final ConcurrentHashMap<Long, Handler> handlers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<Long, Handler<?>> handlers = new ConcurrentHashMap<>();
private final Set<Long> timedOutHandlers = new ConcurrentHashMap<Long, Object>().keySet(new Object()); private final Set<Long> timedOutHandlers = new ConcurrentHashMap<Long, Object>().keySet(new Object());
private final ScheduledExecutorService timers = Executors.newSingleThreadScheduledExecutor(); private final ScheduledExecutorService timers = Executors.newSingleThreadScheduledExecutor();
private final ExceptionHandler defaultExceptionHandler; private final ExceptionHandler defaultExceptionHandler;
private final Handler updateHandler; private final Handler<TdApi.Update> updateHandler;
private final Thread shutdownHook = new Thread(this::onJVMShutdown); private final Thread shutdownHook = new Thread(this::onJVMShutdown);
@ -48,7 +49,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
public InternalReactiveClient(InternalClientManager clientManager) { public InternalReactiveClient(InternalClientManager clientManager) {
this.clientManager = clientManager; this.clientManager = clientManager;
this.updateHandler = new Handler(this::onUpdateFromHandler, this::onUpdateException); this.updateHandler = new Handler<>(this::onUpdateFromHandler, this::onUpdateException);
this.defaultExceptionHandler = this::onDefaultException; this.defaultExceptionHandler = this::onDefaultException;
Runtime.getRuntime().addShutdownHook(shutdownHook); Runtime.getRuntime().addShutdownHook(shutdownHook);
} }
@ -105,7 +106,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
/** /**
* Handles only a response (not an update!) * Handles only a response (not an update!)
*/ */
private void handleResponse(long eventId, TdApi.Object event, Handler handler) { private void handleResponse(long eventId, TdApi.Object event, Handler<?> handler) {
if (handler != null) { if (handler != null) {
try { try {
if (eventId == 0) { if (eventId == 0) {
@ -137,7 +138,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
* Handles a response or an update * Handles a response or an update
*/ */
private void handleEvent(long eventId, TdApi.Object event) { private void handleEvent(long eventId, TdApi.Object event) {
Handler handler = eventId == 0 ? updateHandler : handlers.remove(eventId); Handler<?> handler = eventId == 0 ? updateHandler : handlers.remove(eventId);
handleResponse(eventId, event, handler); handleResponse(eventId, event, handler);
} }
@ -151,7 +152,6 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
} }
} }
@SuppressWarnings({"ReactiveStreamsSubscriberImplementation", "Convert2Diamond"})
public void createAndRegisterClient() { public void createAndRegisterClient() {
if (clientId != null) { if (clientId != null) {
throw new UnsupportedOperationException("Can't initialize the same client twice!"); throw new UnsupportedOperationException("Can't initialize the same client twice!");
@ -164,7 +164,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
} }
@Override @Override
public Publisher<TdApi.Object> send(Function query, Duration responseTimeout) { public <R extends TdApi.Object> Publisher<TdApi.Object> send(Function<R> query, Duration responseTimeout) {
return subscriber -> { return subscriber -> {
Subscription subscription = new Subscription() { Subscription subscription = new Subscription() {
@ -202,7 +202,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
} }
}, responseTimeout.toMillis(), TimeUnit.MILLISECONDS); }, responseTimeout.toMillis(), TimeUnit.MILLISECONDS);
handlers.put(queryId, new Handler(result -> { handlers.put(queryId, new Handler<>(result -> {
logger.trace(TG_MARKER, logger.trace(TG_MARKER,
"Client {} is replying the query id {}: request: {} result: {}", "Client {} is replying the query id {}: request: {} result: {}",
clientId, clientId,
@ -243,7 +243,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
} }
@Override @Override
public TdApi.Object execute(Function query) { public <R extends TdApi.Object> TdApi.Object execute(Function<R> query) {
if (isClosedAndMaybeThrow(query)) { if (isClosedAndMaybeThrow(query)) {
return new TdApi.Ok(); return new TdApi.Ok();
} }
@ -324,7 +324,7 @@ public final class InternalReactiveClient implements ClientEventsHandler, Reacti
* @param function function used to check if the check will be enforced or not. Can be null * @param function function used to check if the check will be enforced or not. Can be null
* @return true if closed * @return true if closed
*/ */
private boolean isClosedAndMaybeThrow(Function function) { private boolean isClosedAndMaybeThrow(Function<?> function) {
boolean closed = alreadyReceivedClosed.get(); boolean closed = alreadyReceivedClosed.get();
if (closed) { if (closed) {
if (function != null && function.getConstructor() == TdApi.Close.CONSTRUCTOR) { if (function != null && function.getConstructor() == TdApi.Close.CONSTRUCTOR) {

View File

@ -10,11 +10,11 @@ final class NativeClientAccess extends NativeClient {
return NativeClientAccess.createNativeClient(); return NativeClientAccess.createNativeClient();
} }
public static TdApi.Object execute(Function function) { public static <R extends TdApi.Object> TdApi.Object execute(Function<R> function) {
return NativeClientAccess.nativeClientExecute(function); return NativeClientAccess.nativeClientExecute(function);
} }
public static void send(int nativeClientId, long eventId, TdApi.Function function) { public static <R extends TdApi.Object> void send(int nativeClientId, long eventId, TdApi.Function<R> function) {
NativeClientAccess.nativeClientSend(nativeClientId, eventId, function); NativeClientAccess.nativeClientSend(nativeClientId, eventId, function);
} }

View File

@ -185,9 +185,10 @@ public final class ResponseReceiver extends Thread implements AutoCloseable {
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private int[] generateSortIndex(int from, int to, int[] data) { private int[] generateSortIndex(int from, int to, int[] data) {
int[] sortedIndices = Arrays.copyOfRange(originalSortingSource, from, to); int[] sortedIndices = Arrays.copyOfRange(originalSortingSource, from, to);
it.unimi.dsi.fastutil.Arrays.mergeSort(from, to, (o1, o2) -> { it.unimi.dsi.fastutil.Arrays.mergeSort(from, to,
return Integer.compare(data[sortedIndices[o1]], data[sortedIndices[o2]]); (o1, o2) -> Integer.compare(data[sortedIndices[o1]], data[sortedIndices[o2]]),
}, new IntSwapper(sortedIndices)); new IntSwapper(sortedIndices)
);
return sortedIndices; return sortedIndices;
} }

View File

@ -17,6 +17,8 @@
package it.tdlight.common.utils; package it.tdlight.common.utils;
import java.io.IOException;
/** /**
* An exception that is thrown when the LoadLibrary class fails to load the library. * An exception that is thrown when the LoadLibrary class fails to load the library.
*/ */
@ -25,6 +27,18 @@ public final class CantLoadLibrary extends Exception {
* Creates a new CantLoadLibrary exception. * Creates a new CantLoadLibrary exception.
*/ */
CantLoadLibrary() { CantLoadLibrary() {
super("FATAL: Init failed when loading TDLib native libraries, execution can't continue"); super("Init failed when loading TDLib native libraries, execution can't continue");
} }
public CantLoadLibrary(String message) {
super(message);
}
public CantLoadLibrary(String message, Exception cause) {
super(message, cause);
}
public CantLoadLibrary(Exception cause) {
super(cause);
}
} }

View File

@ -73,18 +73,16 @@ public final class LoadLibrary {
Os os = getOs(); Os os = getOs();
if (arch == Arch.UNKNOWN) { if (arch == Arch.UNKNOWN) {
throw (CantLoadLibrary) new CantLoadLibrary().initCause(new IllegalStateException( throw new CantLoadLibrary("Arch: \"" + System.getProperty("os.arch") + "\" is unknown");
"Arch: \"" + System.getProperty("os.arch") + "\" is unknown"));
} }
if (os == Os.UNKNOWN) { if (os == Os.UNKNOWN) {
throw (CantLoadLibrary) new CantLoadLibrary().initCause(new IllegalStateException( throw new CantLoadLibrary("Os: \"" + System.getProperty("os.name") + "\" is unknown");
"Os: \"" + System.getProperty("os.name") + "\" is unknown"));
} }
try { try {
loadJarLibrary(libname, arch, os); loadJarLibrary(libname, arch, os);
} catch (IOException | CantLoadLibrary | UnsatisfiedLinkError e) { } catch (CantLoadLibrary | UnsatisfiedLinkError e) {
if (loadSysLibrary(libname)) { if (loadSysLibrary(libname)) {
return; return;
} }
@ -102,8 +100,13 @@ public final class LoadLibrary {
return true; return true;
} }
private static void loadJarLibrary(String libname, Arch arch, Os os) throws IOException, CantLoadLibrary { private static void loadJarLibrary(String libname, Arch arch, Os os) throws CantLoadLibrary {
Path tempPath = Files.createDirectories(librariesPath.resolve("version-" + libsVersion).resolve(libname)); Path tempPath;
try {
tempPath = Files.createDirectories(librariesPath.resolve("version-" + libsVersion).resolve(libname));
} catch (IOException e) {
throw new CantLoadLibrary("Can't create temporary files", e);
}
Path tempFile = Paths.get(tempPath.toString(), libname + getExt(os)); Path tempFile = Paths.get(tempPath.toString(), libname + getExt(os));
Class<?> classForResource = null; Class<?> classForResource = null;
switch (os) { switch (os) {
@ -177,7 +180,7 @@ public final class LoadLibrary {
break; break;
} }
if (classForResource == null) { if (classForResource == null) {
throw new IOException("Native libraries for platform " + os + "-" + arch + " not found!"); throw new CantLoadLibrary("Native libraries for platform " + os + "-" + arch + " not found!");
} }
InputStream libInputStream; InputStream libInputStream;
try { try {
@ -185,12 +188,20 @@ public final class LoadLibrary {
.getDeclaredMethod("getLibraryAsStream") .getDeclaredMethod("getLibraryAsStream")
.invoke(InputStream.class)); .invoke(InputStream.class));
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | NullPointerException e) { } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | NullPointerException e) {
throw new IOException("Native libraries for platform " + os + "-" + arch + " not found!", e); throw new CantLoadLibrary("Native libraries for platform " + os + "-" + arch + " not found!", e);
} }
if (Files.notExists(tempFile)) { if (Files.notExists(tempFile)) {
Files.copy(libInputStream, tempFile); try {
Files.copy(libInputStream, tempFile);
} catch (IOException e) {
throw new CantLoadLibrary("Can't copy native libraries into temporary files", e);
}
}
try {
libInputStream.close();
} catch (IOException e) {
throw new CantLoadLibrary("Can't load the native libraries", e);
} }
libInputStream.close();
System.load(tempFile.toFile().getAbsolutePath()); System.load(tempFile.toFile().getAbsolutePath());
} }
@ -269,19 +280,4 @@ public final class LoadLibrary {
return ".so"; return ".so";
} }
} }
private static String createPath(String... path) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("/");
for (int i = 0; i < path.length; i++) {
stringBuilder.append(path[i]);
if (i < path.length - 1) {
stringBuilder.append("/");
}
}
return stringBuilder.toString();
}
} }

View File

@ -3,6 +3,7 @@ package it.tdlight.common.utils;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.util.concurrent.locks.LockSupport;
public class SpinWaitSupport { public class SpinWaitSupport {
@ -10,5 +11,7 @@ public class SpinWaitSupport {
} }
public static void onSpinWait() { public static void onSpinWait() {
// park for 10ms
LockSupport.parkNanos(10 * 1000L * 1000L);
} }
} }

View File

@ -6,10 +6,11 @@ public class NativeClient {
protected static native int createNativeClient(); protected static native int createNativeClient();
protected static native void nativeClientSend(int nativeClientId, long eventId, TdApi.Function function); protected static native <R extends TdApi.Object> void nativeClientSend(int nativeClientId, long eventId,
TdApi.Function<R> function);
protected static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, protected static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events,
double timeout); double timeout);
protected static native TdApi.Object nativeClientExecute(TdApi.Function function); protected static native <R extends TdApi.Object> TdApi.Object nativeClientExecute(TdApi.Function<R> function);
} }

View File

@ -8,7 +8,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<revision>1.0.0.0-SNAPSHOT</revision> <revision>1.0.0.0-SNAPSHOT</revision>
<api-version>3.3.162</api-version> <api-version>3.3.163</api-version>
<natives-version>3.3.164</natives-version> <natives-version>3.3.164</natives-version>
</properties> </properties>
<repositories> <repositories>

View File

@ -8,7 +8,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<revision>1.0.0.0-SNAPSHOT</revision> <revision>1.0.0.0-SNAPSHOT</revision>
<api-version>3.3.162</api-version> <api-version>3.3.163</api-version>
<natives-version>3.3.164</natives-version> <natives-version>3.3.164</natives-version>
</properties> </properties>
<repositories> <repositories>