summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/example/java
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/example/java')
-rw-r--r--protocols/Telegram/tdlib/td/example/java/.gitignore5
-rw-r--r--protocols/Telegram/tdlib/td/example/java/CMakeLists.txt124
-rw-r--r--protocols/Telegram/tdlib/td/example/java/README.md43
-rw-r--r--protocols/Telegram/tdlib/td/example/java/td_jni.cpp181
4 files changed, 353 insertions, 0 deletions
diff --git a/protocols/Telegram/tdlib/td/example/java/.gitignore b/protocols/Telegram/tdlib/td/example/java/.gitignore
new file mode 100644
index 0000000000..8f846b80d9
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/example/java/.gitignore
@@ -0,0 +1,5 @@
+**/*build/
+bin/
+docs/
+org/drinkless/tdlib/TdApi.java
+td/
diff --git a/protocols/Telegram/tdlib/td/example/java/CMakeLists.txt b/protocols/Telegram/tdlib/td/example/java/CMakeLists.txt
new file mode 100644
index 0000000000..576eee2cfc
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/example/java/CMakeLists.txt
@@ -0,0 +1,124 @@
+cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
+
+if (POLICY CMP0065)
+ # do not export symbols from executables
+ # affects compiler checks in project(), so must be set before it
+ cmake_policy(SET CMP0065 NEW)
+endif()
+
+project(TdJavaExample VERSION 1.0 LANGUAGES CXX)
+
+if (POLICY CMP0054)
+ # do not expand quoted arguments
+ cmake_policy(SET CMP0054 NEW)
+endif()
+if (POLICY CMP0060)
+ # link libraries by full path
+ cmake_policy(SET CMP0060 NEW)
+endif()
+if (POLICY CMP0074)
+ # use environment variables to find libraries
+ cmake_policy(SET CMP0074 NEW)
+endif()
+
+find_package(Td REQUIRED)
+
+if (NOT JNI_FOUND)
+ find_package(JNI REQUIRED)
+endif()
+message(STATUS "Found JNI: ${JNI_INCLUDE_DIRS} ${JNI_LIBRARIES}")
+
+if (NOT Java_FOUND)
+ find_package(Java REQUIRED)
+endif()
+message(STATUS "Found Java: ${Java_JAVAC_EXECUTABLE} ${Java_JAVADOC_EXECUTABLE}")
+
+# Generating TdApi.java
+find_program(PHP_EXECUTABLE php)
+if ((CMAKE_SYSTEM_NAME MATCHES "FreeBSD") AND (CMAKE_SYSTEM_VERSION MATCHES "HBSD"))
+ set(PHP_EXECUTABLE "PHP_EXECUTABLE-NOTFOUND")
+endif()
+
+set(TD_API_JAVA_PACKAGE "org/drinkless/tdlib")
+set(TD_API_JAVA_PATH ${CMAKE_CURRENT_SOURCE_DIR})
+set(TD_API_TLO_PATH ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td/generate/scheme/td_api.tlo)
+set(TD_API_TL_PATH ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td/generate/scheme/td_api.tl)
+set(JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td/generate/JavadocTlDocumentationGenerator.php)
+set(GENERATE_JAVA_API_CMD ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td_generate_java_api TdApi ${TD_API_TLO_PATH} ${TD_API_JAVA_PATH} ${TD_API_JAVA_PACKAGE})
+if (PHP_EXECUTABLE)
+ set(GENERATE_JAVA_API_CMD ${GENERATE_JAVA_API_CMD} && ${PHP_EXECUTABLE} ${JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH} ${TD_API_TL_PATH} ${TD_API_JAVA_PATH}/${TD_API_JAVA_PACKAGE}/TdApi.java)
+endif()
+
+add_custom_target(td_generate_java_api
+ COMMAND ${GENERATE_JAVA_API_CMD}
+ COMMENT "Generating Java TDLib API source files"
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td_generate_java_api ${TD_API_TLO_PATH} ${TD_API_TL_PATH} ${JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH}
+)
+
+set(JAVA_SOURCE_PATH "${TD_API_JAVA_PATH}/${TD_API_JAVA_PACKAGE}")
+get_filename_component(JAVA_OUTPUT_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin REALPATH BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+file(MAKE_DIRECTORY ${JAVA_OUTPUT_DIRECTORY})
+add_custom_target(build_java
+ COMMAND ${Java_JAVAC_EXECUTABLE} -encoding UTF-8 -d ${JAVA_OUTPUT_DIRECTORY} ${JAVA_SOURCE_PATH}/example/Example.java ${JAVA_SOURCE_PATH}/Client.java ${JAVA_SOURCE_PATH}/TdApi.java
+ COMMENT "Building Java code"
+ DEPENDS td_generate_java_api
+)
+
+add_custom_target(generate_javadoc
+ COMMAND ${Java_JAVADOC_EXECUTABLE} -encoding UTF-8 -charset UTF-8 -d ${JAVA_OUTPUT_DIRECTORY}/../docs org.drinkless.tdlib
+ WORKING_DIRECTORY ${TD_API_JAVA_PATH}
+ COMMENT "Generating Javadoc documentation"
+ DEPENDS td_generate_java_api
+)
+
+# Building shared library
+add_library(tdjni SHARED
+ td_jni.cpp
+)
+target_include_directories(tdjni PRIVATE ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2})
+target_link_libraries(tdjni PRIVATE Td::TdStatic ${JAVA_JVM_LIBRARY})
+target_compile_definitions(tdjni PRIVATE PACKAGE_NAME="${TD_API_JAVA_PACKAGE}")
+
+if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ set(GCC 1)
+elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(CLANG 1)
+elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
+ set(INTEL 1)
+elseif (NOT MSVC)
+ message(FATAL_ERROR "Compiler isn't supported")
+endif()
+
+include(CheckCXXCompilerFlag)
+
+if (GCC OR CLANG OR INTEL)
+ if (WIN32 AND INTEL)
+ set(STD14_FLAG /Qstd=c++14)
+ else()
+ set(STD14_FLAG -std=c++14)
+ endif()
+ check_cxx_compiler_flag(${STD14_FLAG} HAVE_STD14)
+ if (NOT HAVE_STD14)
+ string(REPLACE "c++14" "c++1y" STD14_FLAG "${STD14_FLAG}")
+ check_cxx_compiler_flag(${STD14_FLAG} HAVE_STD1Y)
+ set(HAVE_STD14 ${HAVE_STD1Y})
+ endif()
+
+ target_compile_options(tdjni PRIVATE "${STD14_FLAG}")
+elseif (MSVC)
+ set(HAVE_STD14 MSVC_VERSION>=1900)
+endif()
+
+if (NOT HAVE_STD14)
+ message(FATAL_ERROR "No C++14 support in the compiler. Please upgrade the compiler.")
+endif()
+
+add_dependencies(tdjni td_generate_java_api build_java generate_javadoc)
+
+install(TARGETS tdjni
+ LIBRARY DESTINATION bin
+ RUNTIME DESTINATION bin
+)
+if (MSVC AND VCPKG_TOOLCHAIN)
+ install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/" DESTINATION bin FILES_MATCHING PATTERN "*.dll" PATTERN "*.pdb")
+endif()
diff --git a/protocols/Telegram/tdlib/td/example/java/README.md b/protocols/Telegram/tdlib/td/example/java/README.md
new file mode 100644
index 0000000000..bca3e7ad9c
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/example/java/README.md
@@ -0,0 +1,43 @@
+# TDLib Java example
+
+To run this example, you will need installed JDK >= 1.6.
+For Javadoc documentation generation PHP is needed.
+
+You can find complete build instructions for your operating system at https://tdlib.github.io/td/build.html?language=Java.
+
+In general, the build process looks as follows.
+
+TDLib should be prebuilt with JNI bindings and installed to local subdirectory `td/` as follows:
+```
+cd <path to TDLib sources>
+mkdir jnibuild
+cd jnibuild
+cmake -DCMAKE_BUILD_TYPE=Release -DTD_ENABLE_JNI=ON -DCMAKE_INSTALL_PREFIX:PATH=../example/java/td ..
+cmake --build . --target install
+```
+If you want to compile TDLib for 32-bit/64-bit Java on Windows using MSVC, you will also need to add `-A Win32`/`-A x64` option to CMake.
+
+In Windows, use vcpkg toolchain file by adding parameter -DCMAKE_TOOLCHAIN_FILE=<VCPKG_DIR>/scripts/buildsystems/vcpkg.cmake
+
+After this you can compile the example source code:
+```
+cd <path to TDLib sources>/example/java
+mkdir build
+cd build
+cmake -DCMAKE_BUILD_TYPE=Release -DTd_DIR=<full path to TDLib sources>/example/java/td/lib/cmake/Td -DCMAKE_INSTALL_PREFIX:PATH=.. ..
+cmake --build . --target install
+```
+
+Compiled TDLib shared library and Java example after that will be placed in bin/ and Javadoc documentation in `docs/`.
+
+After this you can run the Java example:
+```
+cd <path to TDLib sources>/example/java/bin
+java '-Djava.library.path=.' org/drinkless/tdlib/example/Example
+```
+
+If you receive "Could NOT find JNI ..." error from CMake, you need to specify to CMake path to the installed JDK, for example, "-DJAVA_HOME=/usr/lib/jvm/java-8-oracle/".
+
+If you receive java.lang.UnsatisfiedLinkError with "Can't find dependent libraries", you may also need to copy some dependent shared OpenSSL and zlib libraries to `bin/`.
+
+Make sure that you compiled the example for the same architecture as your JVM.
diff --git a/protocols/Telegram/tdlib/td/example/java/td_jni.cpp b/protocols/Telegram/tdlib/td/example/java/td_jni.cpp
new file mode 100644
index 0000000000..fb7554c747
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/example/java/td_jni.cpp
@@ -0,0 +1,181 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include <td/telegram/Client.h>
+#include <td/telegram/td_api.h>
+
+#include <td/tl/tl_jni_object.h>
+
+#include <cstdint>
+#include <cstdlib>
+#include <string>
+#include <utility>
+
+namespace td_jni {
+
+static td::td_api::object_ptr<td::td_api::Function> fetch_function(JNIEnv *env, jobject function) {
+ td::jni::reset_parse_error();
+ auto result = td::td_api::Function::fetch(env, function);
+ if (td::jni::have_parse_error()) {
+ std::abort();
+ }
+ return result;
+}
+
+static td::ClientManager *get_manager() {
+ return td::ClientManager::get_manager_singleton();
+}
+
+static jint Client_createNativeClient(JNIEnv *env, jclass clazz) {
+ return static_cast<jint>(get_manager()->create_client_id());
+}
+
+static void Client_nativeClientSend(JNIEnv *env, jclass clazz, jint client_id, jlong id, jobject function) {
+ get_manager()->send(static_cast<std::int32_t>(client_id), static_cast<std::uint64_t>(id),
+ fetch_function(env, function));
+}
+
+static jint Client_nativeClientReceive(JNIEnv *env, jclass clazz, jintArray client_ids, jlongArray ids,
+ jobjectArray events, jdouble timeout) {
+ jsize events_size = env->GetArrayLength(ids); // client_ids, ids and events must be of equal size
+ if (events_size == 0) {
+ return 0;
+ }
+ jsize result_size = 0;
+
+ auto *manager = get_manager();
+ auto response = manager->receive(timeout);
+ while (response.object) {
+ auto client_id = static_cast<jint>(response.client_id);
+ env->SetIntArrayRegion(client_ids, result_size, 1, &client_id);
+
+ auto request_id = static_cast<jlong>(response.request_id);
+ env->SetLongArrayRegion(ids, result_size, 1, &request_id);
+
+ jobject object;
+ response.object->store(env, object);
+ env->SetObjectArrayElement(events, result_size, object);
+ env->DeleteLocalRef(object);
+
+ result_size++;
+ if (result_size == events_size) {
+ break;
+ }
+
+ response = manager->receive(0);
+ }
+ return result_size;
+}
+
+static jobject Client_nativeClientExecute(JNIEnv *env, jclass clazz, jobject function) {
+ jobject result;
+ td::ClientManager::execute(fetch_function(env, function))->store(env, result);
+ return result;
+}
+
+static jstring Object_toString(JNIEnv *env, jobject object) {
+ return td::jni::to_jstring(env, to_string(td::td_api::Object::fetch(env, object)));
+}
+
+static jstring Function_toString(JNIEnv *env, jobject object) {
+ return td::jni::to_jstring(env, to_string(td::td_api::Function::fetch(env, object)));
+}
+
+static constexpr jint JAVA_VERSION = JNI_VERSION_1_6;
+static JavaVM *java_vm;
+static jobject log_message_handler;
+
+static void on_log_message(int verbosity_level, const char *log_message) {
+ auto env = td::jni::get_jni_env(java_vm, JAVA_VERSION);
+ if (env == nullptr) {
+ return;
+ }
+
+ jobject handler = env->NewLocalRef(log_message_handler);
+ if (!handler) {
+ return;
+ }
+
+ jclass handler_class = env->GetObjectClass(handler);
+ if (handler_class) {
+ jmethodID on_log_message_method = env->GetMethodID(handler_class, "onLogMessage", "(ILjava/lang/String;)V");
+ if (on_log_message_method) {
+ jstring log_message_str = td::jni::to_jstring(env.get(), log_message);
+ if (log_message_str) {
+ env->CallVoidMethod(handler, on_log_message_method, static_cast<jint>(verbosity_level), log_message_str);
+ env->DeleteLocalRef((jobject)log_message_str);
+ }
+ }
+ env->DeleteLocalRef((jobject)handler_class);
+ }
+
+ env->DeleteLocalRef(handler);
+}
+
+static void Client_nativeClientSetLogMessageHandler(JNIEnv *env, jclass clazz, jint max_verbosity_level,
+ jobject new_log_message_handler) {
+ if (log_message_handler) {
+ td::ClientManager::set_log_message_callback(0, nullptr);
+ jobject old_log_message_handler = log_message_handler;
+ log_message_handler = jobject();
+ env->DeleteGlobalRef(old_log_message_handler);
+ }
+
+ if (new_log_message_handler) {
+ log_message_handler = env->NewGlobalRef(new_log_message_handler);
+ if (!log_message_handler) {
+ // out of memory
+ return;
+ }
+
+ td::ClientManager::set_log_message_callback(static_cast<int>(max_verbosity_level), on_log_message);
+ }
+}
+
+static jint register_native(JavaVM *vm) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast<void **>(&env), JAVA_VERSION) != JNI_OK) {
+ return -1;
+ }
+
+ java_vm = vm;
+
+ auto register_method = [env](jclass clazz, std::string name, std::string signature, auto function_ptr) {
+ td::jni::register_native_method(env, clazz, std::move(name), std::move(signature),
+ reinterpret_cast<void *>(function_ptr));
+ };
+
+ auto client_class = td::jni::get_jclass(env, PACKAGE_NAME "/Client");
+ auto object_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Object");
+ auto function_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Function");
+
+#define TD_OBJECT "L" PACKAGE_NAME "/TdApi$Object;"
+#define TD_FUNCTION "L" PACKAGE_NAME "/TdApi$Function;"
+ register_method(client_class, "createNativeClient", "()I", Client_createNativeClient);
+ register_method(client_class, "nativeClientSend", "(IJ" TD_FUNCTION ")V", Client_nativeClientSend);
+ register_method(client_class, "nativeClientReceive", "([I[J[" TD_OBJECT "D)I", Client_nativeClientReceive);
+ register_method(client_class, "nativeClientExecute", "(" TD_FUNCTION ")" TD_OBJECT, Client_nativeClientExecute);
+ register_method(client_class, "nativeClientSetLogMessageHandler", "(IL" PACKAGE_NAME "/Client$LogMessageHandler;)V",
+ Client_nativeClientSetLogMessageHandler);
+
+ register_method(object_class, "toString", "()Ljava/lang/String;", Object_toString);
+
+ register_method(function_class, "toString", "()Ljava/lang/String;", Function_toString);
+#undef TD_FUNCTION
+#undef TD_OBJECT
+
+ td::jni::init_vars(env, PACKAGE_NAME);
+ td::td_api::set_package_name(PACKAGE_NAME);
+
+ return JAVA_VERSION;
+}
+
+} // namespace td_jni
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
+ static jint jni_version = td_jni::register_native(vm); // call_once
+ return jni_version;
+}