type of function result
- * @param type of function params
- * @return
- * @throws EverSdkException
- */
- public T callEvent(String functionName,
- P params,
- EverSdkSubscription subscription,
- Class resultClass) throws EverSdkException {
- final int requestId = requestCountNextVal();
- this.subscriptions.put(requestId, subscription);
- addRequest(requestId, functionName, params, true);
- return awaitSyncResponse(requestId, resultClass);
+ this.timeout = extractTimeout(clientConfig);
}
/**
* Most used call to EVER-SDK with some output object
*
* @param functionName
- * @param params record of input type, usually ParamsOf...
- * @param resultClass class of output type record, usually ResultOf...class
- * @param
- * @param
+ * @param functionInputs record of input type, usually ParamsOf...
+ * @param resultClass class of output type record, usually ResultOf...class
+ * @param Class of the result object
+ * @param Class of the function params object
+ * @param Class of the AppObject result callbacks
+ * @param Class of the AppObject param calls
* @return output type record, usually ResultOf...
* @throws EverSdkException
*/
- public T call(String functionName, P params, Class resultClass) throws EverSdkException {
+ public CompletableFuture callAsync(final String functionName,
+ final P functionInputs,
+ final Class resultClass,
+ final Consumer eventConsumer,
+ final AppObject appObject) throws EverSdkException {
final int requestId = requestCountNextVal();
- addRequest(requestId, functionName, params, true);
- return awaitSyncResponse(requestId, resultClass);
- }
+ // let's clean previous requests and their native memory sessions
+ cleanup();
- /**
- * Calls to EVER-SDK without outputs
- *
- * @param functionName
- * @param params
- * @param
- * @throws EverSdkException
- */
- public
void callVoid(String functionName, P params) throws EverSdkException {
- final int requestId = requestCountNextVal();
- addRequest(requestId, functionName, params, false);
+ // it's better to explicitly mark requests as void to not recheck this every time in response handler
+ boolean hasResponse = !resultClass.equals(Void.class);
+
+ var request = new RequestData(hasResponse,
+ Arena.ofShared(),
+ new ReentrantLock(),
+ resultClass,
+ new CompletableFuture<>(),
+ eventConsumer,
+ appObject);
+ this.requests.put(requestId, request);
+
+ // with reentrant lock, multiple results on any given single request will be processed one by one
+ request.queueLock().lock();
+ try {
+ var paramsJson = processParams(functionInputs);
+ NativeMethods.tcRequest(this.id, functionName, paramsJson, request.nativeArena(), requestId, this);
+ logger.log(System.Logger.Level.TRACE,
+ () -> EverSdk.LOG_FORMAT.formatted(this.id, requestId, functionName, "SEND", paramsJson));
+ } finally {
+ request.queueLock().unlock(); // снимаем блокировку
+ }
+ if (!hasResponse) {
+ request.responseFuture().complete(null);
+ }
+ return request.responseFuture();
}
public int requestCountNextVal() {
@@ -114,22 +89,66 @@ public int requestCountNextVal() {
}
public void addResponse(int requestId, String responseString) {
- if (this.requests.get(requestId).hasResponse()) {
- if (this.responses.containsKey(requestId)) {
- this.responses.get(requestId).complete(responseString);
- } else {
+ var request = this.requests.get(requestId);
+ if (request.hasResponse()) {
+ request.queueLock().lock();
+ try {
+ if (!request.responseFuture().isDone()) {
+ logger.log(System.Logger.Level.TRACE,
+ () -> "CTX:%d REQ:%d RESP:%s".formatted(this.id,requestId, responseString));
+ request.responseFuture()
+ .complete(JsonContext.SDK_JSON_MAPPER().readValue(responseString, request.responseClass()));
+ } else {
+ logger.log(System.Logger.Level.ERROR,
+ "Slot for this request not found on processing response! CTX:%d REQ:%d RESP:%s".formatted(
+ this.id,
+ requestId,
+ responseString));
+ }
+ } catch (JsonProcessingException ex2) {
+ // successful response but parsing failed
logger.log(System.Logger.Level.ERROR,
- "Slot for this request not found on processing response! CTX:%d REQ:%d RESP:%s".formatted(
+ () -> "CTX:%d REQ:%d EVER-SDK Response deserialization failed! %s".formatted(
this.id,
requestId,
- responseString));
+ ex2.toString()));
+ request.responseFuture()
+ .completeExceptionally(new EverSdkException(new EverSdkException.ErrorResult(-500,
+ "EVER-SDK response deserialization failed!"),
+ ex2.getCause()));
+ } finally {
+ request.queueLock().unlock(); // снимаем блокировку
}
}
}
- public void addError(int requestId, String responseString) {
- if (this.responses.containsKey(requestId)) {
- this.responses.get(requestId).completeExceptionally(new CompletionException(responseString, null));
+ private void addError(int requestId, String responseString) {
+ var request = requests.get(requestId);
+ if (request.responseFuture() instanceof CompletableFuture> future) {
+ request.queueLock().lock();
+ try {
+ // These errors are sent by SDK, response_type=1
+ //String everSdkError = ex.getCause().getMessage();
+ logger.log(System.Logger.Level.WARNING,
+ () -> "CTX:%d REQ:%d ERR:%s".formatted(this.id, requestId, responseString));
+ // let's try to parse error response
+ EverSdkException.ErrorResult sdkResponse = JsonContext.SDK_JSON_MAPPER()
+ .readValue(responseString,
+ EverSdkException.ErrorResult.class);
+ future.completeExceptionally(new EverSdkException(sdkResponse));
+ } catch (JsonProcessingException ex1) {
+ // if error response parsing failed
+ logger.log(System.Logger.Level.ERROR,
+ () -> "CTX:%d REQ:%d EVER-SDK Error deserialization failed! %s".formatted(
+ this.id,
+ requestId,
+ ex1.toString()));
+ future.completeExceptionally(new EverSdkException(new EverSdkException.ErrorResult(-500,
+ "EVER-SDK Error deserialization failed!"),
+ ex1.getCause()));
+ } finally {
+ request.queueLock().unlock(); // снимаем блокировку
+ }
} else {
logger.log(System.Logger.Level.ERROR,
"Slot for this request not found on processing error response! CTX:%d REQ:%d ERR:%s".formatted(
@@ -140,9 +159,21 @@ public void addError(int requestId, String responseString) {
}
- public void addEvent(int requestId, String responseString) {
- if (this.subscriptions.containsKey(requestId)) {
- this.subscriptions.get(requestId).acceptEvent(requestId, responseString);
+ private void addEvent(int requestId, String responseString) {
+ var request = this.requests.get(requestId);
+ if (request.subscriptionHandler() instanceof Consumer> handler) {
+ try {
+ JsonNode node = JsonContext.ABI_JSON_MAPPER().readTree(responseString);
+ try {
+ ((Consumer) handler).accept(node);
+ } catch (Exception ex1) {
+ logger.log(System.Logger.Level.ERROR,
+ () -> "REQ:%d EVENT:%s Subscribe Event Action processing failed! %s".formatted(requestId, responseString, ex1.toString()));
+ }
+ } catch (JsonProcessingException ex2) {
+ logger.log(System.Logger.Level.ERROR,
+ () -> "REQ:%d EVENT:%s Subscribe Event JSON deserialization failed! %s".formatted(requestId, responseString, ex2.toString()));
+ }
} else {
logger.log(System.Logger.Level.ERROR,
"Slot for this request not found on processing subscription event! CTX:%d REQ:%d EVENT:%s".formatted(
@@ -152,26 +183,25 @@ public void addEvent(int requestId, String responseString) {
}
}
- public void finishRequest(int requestId) {
- if (this.requests.get(requestId) instanceof RequestData r) {
- if (r.nativeArena() instanceof Arena a) {
- a.close();
- }
- }
- this.requests.remove(requestId);
-/* requestRemoveQueue.add(requestId);
- if (requestRemoveQueue.size() > 10) {
- int removedRequestId = requestRemoveQueue.poll();
- logger.log(System.Logger.Level.TRACE, "Removed REQ:%d".formatted(removedRequestId));
-
+ private void finishRequest(int requestId) {
+ if (this.requests.get(requestId) instanceof RequestData request) {
+ this.requests.remove(requestId);
+ this.cleanupQueue.add(request);
}
- logger.log(System.Logger.Level.DEBUG,
- () -> "Requests current size: " + this.requests.size());*/
this.subscriptions.remove(requestId);
}
- public void finishResponse(int requestId) {
- this.responses.remove(requestId);
+ private void cleanup() {
+ while (this.cleanupQueue.poll() instanceof RequestData request) {
+ if (request.nativeArena() instanceof Arena a) {
+ request.queueLock().lock();
+ try {
+ a.close();
+ } finally {
+ request.queueLock().unlock(); // снимаем блокировку
+ }
+ }
+ }
}
private String processParams(P params) {
@@ -184,81 +214,79 @@ private
String processParams(P params) {
}
}
- private long callTimeout() {
- return Optional.ofNullable(this.clientConfig.network())
- .map(config -> Optional.ofNullable(config.queryTimeout()).orElse(60000L))
- .orElse(60000L);
+ private long extractTimeout(Client.ClientConfig cfg) {
+ return switch (cfg) {
+ case null -> 60000L;
+ case Client.ClientConfig cl -> switch (cl.network()) {
+ case null -> 60000L;
+ case Client.NetworkConfig ntwrk -> Objects.requireNonNullElse(ntwrk.queryTimeout(),60000L);
+ };
+ };
}
- private R awaitSyncResponse(int requestId, Class resultClass) throws EverSdkException {
+ private R awaitSyncResponse(CompletableFuture requestId, Class resultClass) throws EverSdkException {
try {
// waiting for response synchronously
- String responseString = this.responses.get(requestId).get(callTimeout(), TimeUnit.MILLISECONDS);
- logger.log(System.Logger.Level.TRACE, () -> STR."CTX:\{this.id} REQ:\{requestId} RESP:\{responseString}");
+ String responseString = (String) this.requests.get(requestId).responseFuture().get(this.timeout, TimeUnit.MILLISECONDS);
+ logger.log(System.Logger.Level.TRACE, () -> "CTX:%d REQ:%d RESP:%s".formatted(
+ this.id,
+ requestId,
+ responseString));
// let's try to parse response
return JsonContext.SDK_JSON_MAPPER().readValue(responseString, resultClass);
- } catch (ExecutionException ex) {
- try {
- // These errors are sent by SDK, response_type=1
- String everSdkError = ex.getCause().getMessage();
- logger.log(System.Logger.Level.WARNING,
- () -> STR."CTX:\{this.id} REQ:\{requestId} ERR:\{everSdkError}");
- // let's try to parse error response
- EverSdkException.ErrorResult sdkResponse = JsonContext.SDK_JSON_MAPPER()
- .readValue(everSdkError,
- EverSdkException.ErrorResult.class);
- throw new EverSdkException(sdkResponse, ex);
- } catch (JsonProcessingException ex1) {
- // if error response parsing failed
- logger.log(System.Logger.Level.ERROR,
- () -> STR."CTX:\{this.id} REQ:\{requestId} EVER-SDK Error deserialization failed! \{ex1.toString()}");
- throw new EverSdkException(new EverSdkException.ErrorResult(-500,
- "EVER-SDK Error deserialization failed!"),
- ex1.getCause());
- }
- } catch (JsonProcessingException ex2) {
- // successful response but parsing failed
- logger.log(System.Logger.Level.ERROR,
- () -> STR."CTX:\{this.id} REQ:\{requestId} EVER-SDK Response deserialization failed! \{ex2.toString()}");
- throw new EverSdkException(new EverSdkException.ErrorResult(-500,
- "EVER-SDK response deserialization failed!"),
- ex2.getCause());
} catch (InterruptedException ex3) {
logger.log(System.Logger.Level.ERROR,
- () -> STR."CTX:\{this.id} REQ:\{requestId} EVER-SDK Call interrupted! \{ex3.toString()}");
+ () -> "CTX:%d REQ:%d EVER-SDK Call interrupted! %s}".formatted(
+ this.id,
+ requestId,
+ ex3.toString()));
throw new EverSdkException(new EverSdkException.ErrorResult(-400, "EVER-SDK call interrupted!"),
ex3.getCause());
} catch (TimeoutException ex4) {
logger.log(System.Logger.Level.ERROR,
- () -> STR."CTX:\{this.id} REQ:\{requestId} EVER-SDK Call expired on Timeout! \{ex4.toString()}");
+ () -> "CTX:%d REQ:%d EVER-SDK Call expired on Timeout! %s".formatted(
+ this.id,
+ requestId,
+ ex4.toString()));
throw new EverSdkException(new EverSdkException.ErrorResult(-408, "EVER-SDK call expired on Timeout!"),
ex4.getCause());
- } finally {
- finishResponse(requestId);
+ } catch (JsonMappingException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
}
}
- /**
- * Most used call to EVER-SDK with some output object
- *
- * @param functionName
- * @param params record of input type, usually ParamsOf...
- * @param
- * @throws EverSdkException
- */
- private
void addRequest(int requestId, String functionName, P params, boolean hasResponse) {
- //final NativeUpcallHandler handler = new NativeUpcallHandler(this.id, hasResponse);
- var arena = Arena.ofShared();
- this.requests.put(requestId, new RequestData(hasResponse, Arena.ofShared()));
- if (hasResponse) {
- this.responses.put(requestId, new CompletableFuture<>());
- }
- var paramsJson = processParams(params);
- NativeMethods.tcRequest(this.id, functionName, paramsJson, arena, requestId, this);
- logger.log(System.Logger.Level.TRACE,
- () -> EverSdk.LOG_FORMAT.formatted(this.id, requestId, functionName, "SEND", paramsJson));
- }
+// /**
+// * Most used call to EVER-SDK with some output object
+// *
+// * @param functionName
+// * @param params record of input type, usually ParamsOf...
+// * @param
+// * @throws EverSdkException
+// */
+// private
void addRequest(int requestId, String functionName, P params, boolean hasResponse) {
+// //final NativeUpcallHandler handler = new NativeUpcallHandler(this.id, hasResponse);
+// var arena = Arena.ofShared();
+// var request = new RequestData(hasResponse, arena, new ReentrantLock());
+// cleanup();
+// this.requests.put(requestId, request);
+// request.queueLock().lock();
+// try {
+// if (hasResponse) {
+// this.responses.put(requestId, new CompletableFuture<>());
+// }
+// var paramsJson = processParams(params);
+// NativeMethods.tcRequest(this.id, functionName, paramsJson, arena, requestId, this);
+// logger.log(System.Logger.Level.TRACE,
+// () -> EverSdk.LOG_FORMAT.formatted(this.id, requestId, functionName, "SEND", paramsJson));
+// } finally {
+// request.queueLock().unlock(); // снимаем блокировку
+// }
+// }
public int id() {
return this.id;
@@ -286,18 +314,20 @@ public Client.ClientConfig config() {
*/
@Override
public void apply(int request_id, MemorySegment params_json, int response_type, boolean finished) {
- try (var arena = Arena.ofShared()) {
- final String responseString = NativeStrings.toJava(params_json, arena);
+ try {
+ final String responseString = NativeStrings.toJava(params_json);
if (logger.isLoggable(System.Logger.Level.TRACE)) {
logger.log(System.Logger.Level.TRACE,
- STR."REQ:\{request_id} TYPE:\{response_type} FINISHED:\{finished} JSON:\{responseString}");
+ "REQ:%d TYPE:%d FINISHED:%s JSON:%s".formatted(
+ this.id,
+ response_type,
+ String.valueOf(finished),
+ responseString));
}
- if (response_type == ton_client.tc_response_success()) {
- addResponse(request_id, responseString);
- } else if (response_type == ton_client.tc_response_error()) {
- addError(request_id, responseString);
- } else if (response_type >= 100) {
- addEvent(request_id, responseString);
+ switch (tc_response_types.of(response_type)) {
+ case tc_response_types.TC_RESPONSE_SUCCESS -> addResponse(request_id, responseString);
+ case tc_response_types.TC_RESPONSE_ERROR -> addError(request_id, responseString);
+ case tc_response_types.TC_RESPONSE_CUSTOM -> addEvent(request_id, responseString);
}
// if "final" flag received, let's remove everything
@@ -306,10 +336,19 @@ public void apply(int request_id, MemorySegment params_json, int response_type,
}
} catch (Exception e) {
logger.log(System.Logger.Level.ERROR,
- STR."REQ:\{request_id} TYPE:\{response_type} EVER-SDK Unexpected upcall error! \{e.toString()}");
+ "REQ:%d TYPE:%d EVER-SDK Unexpected upcall error! %s".formatted(request_id, response_type, e.toString()));
}
}
- record RequestData(boolean hasResponse, Arena nativeArena) {
+ record RequestData(boolean hasResponse,
+ Arena nativeArena,
+ ReentrantLock queueLock,
+ Class responseClass,
+ CompletableFuture responseFuture,
+ Consumer subscriptionHandler,
+ AppObject appObject) {
+ }
+
+ record SubscriptionHandler(Consumer eventAction) {
}
}
diff --git a/src/main/java/tech/deplant/java4ever/binding/ffi/EverSdkSubscription.java b/src/main/java/tech/deplant/java4ever/binding/ffi/EverSdkSubscription.java
index 7e1a9c7..1f11a80 100644
--- a/src/main/java/tech/deplant/java4ever/binding/ffi/EverSdkSubscription.java
+++ b/src/main/java/tech/deplant/java4ever/binding/ffi/EverSdkSubscription.java
@@ -35,11 +35,11 @@ void acceptEvent(int requestId, String responseString) {
this.eventAction.accept(node);
} catch (Exception ex1) {
logger.log(System.Logger.Level.ERROR,
- () -> STR."REQ:\{requestId} EVENT: \{responseString} Subscribe Event Action processing failed! \{ex1.toString()}");
+ () -> "REQ:%d EVENT:%s Subscribe Event Action processing failed! %s".formatted(requestId, responseString, ex1.toString()));
}
} catch (JsonProcessingException ex2) {
logger.log(System.Logger.Level.ERROR,
- () -> STR."REQ:\{requestId} EVENT: \{responseString} Subscribe Event JSON deserialization failed! \{ex2.toString()}");
+ () -> "REQ:%d EVENT:%s Subscribe Event JSON deserialization failed! %s".formatted(requestId, responseString, ex2.toString()));
}
}
diff --git a/src/main/java/tech/deplant/java4ever/binding/ffi/NativeMethods.java b/src/main/java/tech/deplant/java4ever/binding/ffi/NativeMethods.java
index 9526983..a38a6bc 100644
--- a/src/main/java/tech/deplant/java4ever/binding/ffi/NativeMethods.java
+++ b/src/main/java/tech/deplant/java4ever/binding/ffi/NativeMethods.java
@@ -2,6 +2,7 @@
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
+import java.lang.foreign.SegmentAllocator;
public class NativeMethods {
@@ -10,22 +11,25 @@ public static String tcCreateContext(String configJson) {
MemorySegment handle = ton_client.tc_create_context(NativeStrings.toRust(configJson, nativeMemory));
String s = NativeStrings.toJava(
ton_client.tc_read_string(
- RuntimeHelper.CONSTANT_ALLOCATOR,
+ nativeMemory::allocate,
handle
- ),
- nativeMemory
+ )
);
ton_client.tc_destroy_string(handle);
return s;
}
}
+ public static void tcDestroyContext(int contextId) {
+ ton_client.tc_destroy_context(contextId);
+ }
+
public static void tcRequest(int contextId,
String functionName,
String params,
Arena nativeMemory,
int requestId,
- tc_response_handler_t handler) {
+ tc_response_handler_t.Function handler) {
ton_client.tc_request(contextId,
NativeStrings.toRust(functionName, nativeMemory),
NativeStrings.toRust(params, nativeMemory),
diff --git a/src/main/java/tech/deplant/java4ever/binding/ffi/NativeStrings.java b/src/main/java/tech/deplant/java4ever/binding/ffi/NativeStrings.java
index 0fe9fa5..0caa7f8 100644
--- a/src/main/java/tech/deplant/java4ever/binding/ffi/NativeStrings.java
+++ b/src/main/java/tech/deplant/java4ever/binding/ffi/NativeStrings.java
@@ -8,24 +8,19 @@
import static tech.deplant.java4ever.binding.ffi.tc_string_data_t.*;
class NativeStrings {
- public static MemorySegment toRust(final String text, final Arena arena) {
- MemorySegment nativeString = arena.allocateUtf8String(text);
- MemorySegment stringDataSegment = arena.allocate(constants$0.const$0);
- content$set(stringDataSegment, 0, nativeString); //TODO dubious, recheck
- len$set(stringDataSegment,
- 0,
- ((int) nativeString.byteSize()) - 1
- // minus 1 because last symbol is u0000 in UTF-8
- );
+ public static MemorySegment toRust(final String text, final Arena nativeSession) {
+ MemorySegment nativeString = nativeSession.allocateFrom(text);
+ int strlen = ((int) nativeString.byteSize()) - 1; // minus 1 because last symbol is u0000 in UTF-8
+ MemorySegment stringDataSegment = nativeSession.allocate(tc_string_data_t.layout());
+ tc_string_data_t.content(stringDataSegment, nativeString);
+ tc_string_data_t.len(stringDataSegment, strlen);
return stringDataSegment;
}
- public static String toJava(MemorySegment seg, Arena arena) {
- if (tc_string_data_t.len$get(seg) > 0) {
- MemorySegment contentAddress = tc_string_data_t.content$get(seg);
- MemorySegment content = contentAddress.asSlice(0, len$get(seg));
- byte[] str = content.toArray(JAVA_BYTE);
- return new String(str, StandardCharsets.UTF_8);
+ public static String toJava(MemorySegment seg) {
+ if (tc_string_data_t.len(seg) > 0) {
+ final MemorySegment content = tc_string_data_t.content(seg).asSlice(0, len(seg));
+ return new String(content.toArray(JAVA_BYTE), StandardCharsets.UTF_8);
} else {
return "";
}
diff --git a/src/main/java/tech/deplant/java4ever/binding/ffi/tc_response_types.java b/src/main/java/tech/deplant/java4ever/binding/ffi/tc_response_types.java
new file mode 100644
index 0000000..93fc543
--- /dev/null
+++ b/src/main/java/tech/deplant/java4ever/binding/ffi/tc_response_types.java
@@ -0,0 +1,44 @@
+package tech.deplant.java4ever.binding.ffi;
+
+/**
+ * {@snippet lang = c:
+ enum tc_response_types_t {
+ tc_response_success = 0,
+ tc_response_error = 1,
+ tc_response_nop = 2,
+ tc_response_app_request = 3,
+ tc_response_app_notify = 4,
+ tc_response_custom >= 100,
+ };
+ *}
+ */
+enum tc_response_types {
+
+ TC_RESPONSE_SUCCESS,
+ TC_RESPONSE_ERROR,
+ TC_RESPONSE_NOP,
+ TC_RESPONSE_APP_REQUEST,
+ TC_RESPONSE_APP_NOTIFY,
+ TC_RESPONSE_CUSTOM,
+ TC_RESPONSE_RESERVED;
+
+ public static tc_response_types of(int val) {
+ tc_response_types result;
+ if (val == 0) {
+ result = TC_RESPONSE_SUCCESS;
+ } else if (val == 1) {
+ result = TC_RESPONSE_ERROR;
+ } else if (val == 2) {
+ result = TC_RESPONSE_NOP;
+ } else if (val == 3) {
+ result = TC_RESPONSE_APP_REQUEST;
+ } else if (val == 4) {
+ result = TC_RESPONSE_APP_NOTIFY;
+ } else if (val >= 100) {
+ result = TC_RESPONSE_CUSTOM;
+ } else {
+ result = TC_RESPONSE_RESERVED;
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/tech/deplant/java4ever/binding/generator/ParserEngine.java b/src/main/java/tech/deplant/java4ever/binding/generator/ParserEngine.java
index a68f24f..cad34b0 100644
--- a/src/main/java/tech/deplant/java4ever/binding/generator/ParserEngine.java
+++ b/src/main/java/tech/deplant/java4ever/binding/generator/ParserEngine.java
@@ -119,6 +119,9 @@ public static void parse(ApiReference parsedApiReference) throws IOException {
// main file building loop
// loops modules again, now to write them
for (var module : parsedApiReference.modules()) {
+ if ("debot".equals(module.name())) {
+ continue;
+ }
final TypeSpec.Builder moduleBuilder = moduleToBuilder(module,
ParserUtils.capitalize(module.name()),
apiVersion);
diff --git a/src/main/java/tech/deplant/java4ever/binding/generator/jtype/SdkFunction.java b/src/main/java/tech/deplant/java4ever/binding/generator/jtype/SdkFunction.java
index af47b28..b9bb42c 100644
--- a/src/main/java/tech/deplant/java4ever/binding/generator/jtype/SdkFunction.java
+++ b/src/main/java/tech/deplant/java4ever/binding/generator/jtype/SdkFunction.java
@@ -8,16 +8,13 @@
import tech.deplant.java4ever.binding.generator.ParserEngine;
import tech.deplant.java4ever.binding.generator.ParserUtils;
import tech.deplant.java4ever.binding.generator.TypeReference;
+import tech.deplant.java4ever.binding.generator.reference.*;
import tech.deplant.javapoet.*;
-import tech.deplant.java4ever.binding.generator.reference.ApiFunction;
-import tech.deplant.java4ever.binding.generator.reference.ApiType;
-import tech.deplant.java4ever.binding.generator.reference.StructType;
import javax.lang.model.element.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -76,7 +73,7 @@ public MethodSpec.Builder poeticize() {
// adds function name as a first arg to statementArgs array
statementArgs.add(String.format("%s.%s", functionModule().toLowerCase(), function().name()));
// call template for all variants
- String templateString = "%RETURN_KEY%EverSdk.%CALL_TYPE%(ctxId, $S, %PARAMS%%APP_OBJ%%RETURN_CLASS%)";
+ String templateString = "%RETURN_KEY%EverSdk.%CALL_TYPE%(ctxId, $S, %PARAMS%%RETURN_CLASS%%APP_OBJ%)";
methodBuilder.addParameter(ClassName.INT, "ctxId");
for (ApiType param : function().params()) {
logger.log(System.Logger.Level.TRACE, () -> function().name() + "\\" + param.name() + "\\" + param.type());
@@ -91,16 +88,24 @@ public MethodSpec.Builder poeticize() {
parsedParam));
}
case "callback" -> {
- templateString = templateString.replace("%APP_OBJ%", ", eventHandler");
- templateString = templateString.replace("%CALL_TYPE%", "callEvent");
+ templateString = templateString.replace("%APP_OBJ%", ", callback");
+ templateString = templateString.replace("%CALL_TYPE%", "asyncCallback");
var paramClass = ClassName.get(JsonNode.class);//ClassName.get(CallbackHandler.class);
- //methodBuilder.addParameter(ParameterizedTypeName.get(ClassName.get(Consumer.class), paramClass), "eventHandler");
- methodBuilder.addParameter(ClassName.get(EverSdkSubscription.class), "eventHandler");
+ methodBuilder.addParameter(ParameterizedTypeName.get(ClassName.get(Consumer.class), paramClass), "callback");
+ //methodBuilder.addParameter(ClassName.get(EverSdkSubscription.class), "eventHandler");
}
case "app_object", "password_provider" -> {
templateString = templateString.replace("%APP_OBJ%", ", appObject");
- templateString = templateString.replace("%CALL_TYPE%", "callAppObject");
- methodBuilder.addParameter(ClassName.get(AppSigningBox.class), "appObject");
+ templateString = templateString.replace("%CALL_TYPE%", "asyncAppObject");
+ if (param instanceof GenericType gen) {
+ TypeName[] appObjectParams = Arrays.stream(gen.generic_args()).map(arg -> switch(arg) {
+ case null -> null;
+ case RefType ref -> SdkParam.ofApiType(ref, typeLibrary());
+ default -> null;
+ }).filter(Objects::nonNull).map(SdkParam::refClassName).toArray(TypeName[]::new);
+ methodBuilder.addParameter(ParameterizedTypeName.get(ClassName.bestGuess(gen.generic_name()), appObjectParams),
+ "appObject");
+ }
}
default -> logger.log(System.Logger.Level.WARNING, () -> "Unknown parameter: " + param.name());
}
@@ -114,18 +119,18 @@ public MethodSpec.Builder poeticize() {
if (!resultReference.isVoid()) {
//
templateString = templateString.replace("%RETURN_KEY%", "return ");
- templateString = templateString.replace("%CALL_TYPE%", "call");
+ templateString = templateString.replace("%CALL_TYPE%", "async");
templateString = templateString.replace("%RETURN_CLASS%", ", $T.class");
var typeName = resultReference.toTypeName();
// adds return class to method builder
- methodBuilder.returns(typeName);
+ methodBuilder.returns(ParameterizedTypeName.get(ClassName.get(CompletableFuture.class), typeName));
// adds return class as a final arg to statementArgs array
statementArgs.add(typeName);
}
}
// void result
templateString = templateString.replace("%RETURN_KEY%", "");
- templateString = templateString.replace("%CALL_TYPE%", "callVoid");
+ templateString = templateString.replace("%CALL_TYPE%", "asyncVoid");
templateString = templateString.replace("%RETURN_CLASS%", "");
final String finalTemplateString = templateString;
diff --git a/src/main/java/tech/deplant/java4ever/binding/generator/jtype/SdkParam.java b/src/main/java/tech/deplant/java4ever/binding/generator/jtype/SdkParam.java
index 8e990f0..38d8374 100644
--- a/src/main/java/tech/deplant/java4ever/binding/generator/jtype/SdkParam.java
+++ b/src/main/java/tech/deplant/java4ever/binding/generator/jtype/SdkParam.java
@@ -10,6 +10,7 @@
import tech.deplant.java4ever.binding.generator.reference.ApiType;
import java.util.Map;
+import java.util.Objects;
public record SdkParam(TypeName refClassName,
String parameterName,
@@ -31,28 +32,36 @@ public static SdkParam ofApiType(ApiType paramType,
var typeReference = TypeReference.fromApiType(paramType);
var javaType = typeReference.toTypeDeclaration(typeLibrary);
TypeName className;
+ String paramName = "";
+ String reservedName = "";
+ boolean hasReserved = false;
+
+
if (javaType instanceof SdkDummy dummy) {
className = TypeReference.fromApiType(dummy.type()).toTypeName();
} else {
className = typeReference.toTypeName();
}
- String paramName;
+
if ("Context".equals(className.toString())) {
paramName = "ctx";
} else {
paramName = paramType.name(); //ParserUtils.camelCase(paramType.name());
}
- // PARAM NAME
- //String paramName = ParserUtils.camelCase(paramType.name()); // camel cased, can clash with reserved
- String reservedName = RESERVED_FIELD_NAMES.getOrDefault(paramName,
- paramName); // checks for reserved words or defaults to paramName
+ if (Objects.nonNull(paramName)) {
+ // PARAM NAME
+ //String paramName = ParserUtils.camelCase(paramType.name()); // camel cased, can clash with reserved
- boolean hasReserved = false;
- if (!reservedName.equals(paramName)) {
- hasReserved = true;
+ reservedName = RESERVED_FIELD_NAMES.getOrDefault(paramName, paramName); // checks for reserved words or defaults to paramName
+
+ hasReserved = false;
+ if (!reservedName.equals(paramName)) {
+ hasReserved = true;
+ }
}
+
return new SdkParam(className,
ParserUtils.camelCase(reservedName),
paramName,
diff --git a/src/test/java/tech/deplant/java4ever/unit/ClientTests.java b/src/test/java/tech/deplant/java4ever/unit/ClientTests.java
index 87fa9cd..3d1ba23 100644
--- a/src/test/java/tech/deplant/java4ever/unit/ClientTests.java
+++ b/src/test/java/tech/deplant/java4ever/unit/ClientTests.java
@@ -1,7 +1,5 @@
package tech.deplant.java4ever.unit;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.yegor256.OnlineMeans;
import com.yegor256.WeAreOnline;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayNameGeneration;
@@ -15,11 +13,14 @@
import tech.deplant.java4ever.binding.Client;
import tech.deplant.java4ever.binding.EverSdk;
import tech.deplant.java4ever.binding.EverSdkException;
-import tech.deplant.java4ever.binding.loader.AbsolutePathLoader;
import tech.deplant.java4ever.binding.loader.DefaultLoader;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@Execution(ExecutionMode.CONCURRENT)
@@ -30,21 +31,40 @@ public class ClientTests {
@BeforeAll
public static void loadSdk() {
- EverSdk.load(new AbsolutePathLoader("c:/opt/sdk/ton_client.dll"));
+ TestEnv.loadEverSdk();
}
@Test
- @OnlineMeans(url = TestEnv.NODESE_URL, connectTimeout = 500, readTimeout = 1500)
- public void sdk_version_equals_constant() throws EverSdkException {
- int ctxId = TestEnv.newContext();
- assertEquals(DefaultLoader.EVER_SDK_VERSION, Client.version(ctxId).version());
+ public void sdk_version_equals_constant() throws EverSdkException, ExecutionException, InterruptedException {
+ int ctxId = TestEnv.newContextEmpty();
+ assertEquals(DefaultLoader.EVER_SDK_VERSION, Client.version(ctxId).get().version());
}
@Test
- @OnlineMeans(url = TestEnv.NODESE_URL, connectTimeout = 500, readTimeout = 1500)
- public void api_reference_version_equals_constant() throws EverSdkException {
- int ctxId = TestEnv.newContext();
- assertEquals(DefaultLoader.EVER_SDK_VERSION, Client.getApiReference(ctxId).api().get("version").asText());
+ public void api_reference_version_equals_constant() throws EverSdkException, ExecutionException, InterruptedException {
+ int ctxId = TestEnv.newContextEmpty();
+ assertEquals(DefaultLoader.EVER_SDK_VERSION, Client.getApiReference(ctxId).get().api().get("version").asText());
}
+// @Test
+// public void api_reference_memory_consumption_should_be_stable() throws EverSdkException, InterruptedException {
+// int ctxId = TestEnv.newContextEmpty();
+// try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
+// for (int i = 0; i < 10000; i++) {
+// //CompletableFuture result = //Client.getApiReference(ctxId);
+// CompletableFuture result = EverSdk.async(ctxId,
+// "client.get_api_reference",
+// null,
+// Client.ResultOfGetApiReference.class,
+// null);
+// EverSdk.await(EverSdk.async(ctxId,"client.get_api_reference",
+// null,
+// Client.ResultOfGetApiReference.class,
+// null));
+// executor.submit(() -> result.get());
+// }
+// executor.awaitTermination(100, TimeUnit.SECONDS);
+// }
+// }
+
}
\ No newline at end of file
diff --git a/src/test/java/tech/deplant/java4ever/unit/ConfigTests.java b/src/test/java/tech/deplant/java4ever/unit/ContextTests.java
similarity index 79%
rename from src/test/java/tech/deplant/java4ever/unit/ConfigTests.java
rename to src/test/java/tech/deplant/java4ever/unit/ContextTests.java
index ebe9c43..cb18326 100644
--- a/src/test/java/tech/deplant/java4ever/unit/ConfigTests.java
+++ b/src/test/java/tech/deplant/java4ever/unit/ContextTests.java
@@ -1,7 +1,6 @@
package tech.deplant.java4ever.unit;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.yegor256.OnlineMeans;
import com.yegor256.WeAreOnline;
import org.junit.jupiter.api.BeforeAll;
@@ -18,26 +17,31 @@
import tech.deplant.java4ever.binding.loader.DefaultLoader;
import java.io.IOException;
+import java.util.concurrent.ExecutionException;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@Execution(ExecutionMode.CONCURRENT)
@ExtendWith(WeAreOnline.class)
-public class ConfigTests {
+public class ContextTests {
- private static final Logger log = LoggerFactory.getLogger(ConfigTests.class);
+ private static final Logger log = LoggerFactory.getLogger(ContextTests.class);
@BeforeAll
public static void loadSdk() {
- EverSdk.load(new AbsolutePathLoader("c:/opt/sdk/ton_client.dll"));
+ TestEnv.loadEverSdk();
+ }
+
+ @Test
+ public void create_and_destroy() {
+ EverSdk.destroy(TestEnv.newContextEmpty());
}
@Test
@OnlineMeans(url = TestEnv.NODESE_URL, connectTimeout = 500, readTimeout = 1500)
public void context_id_increments_as_we_open_new_contexts() {
- int ctxId = TestEnv.newContext();
+ int ctxId = TestEnv.newContextEmpty();
int startId = ctxId;
assertTrue(startId >= 1);
ctxId = TestEnv.newContext();
@@ -58,14 +62,14 @@ public void config_from_builder_equals_config_from_json() throws IOException {
@Test
@OnlineMeans(url = TestEnv.NODESE_URL, connectTimeout = 500, readTimeout = 1500)
- public void binding_config_from_json_rewrites_to_preset_config() throws IOException, EverSdkException {
+ public void binding_config_from_json_rewrites_to_preset_config() throws IOException, EverSdkException, ExecutionException, InterruptedException {
var configJson = "{\"binding\":{\"library\":\"ton-client-java\",\"version\":\"1.5.0\"}}";
int ctxId = EverSdk.builder().build().orElseThrow();
int ctxId2 = EverSdk.createWithJson(configJson).orElseThrow();
- assertEquals(DefaultLoader.BINDING_LIBRARY_VERSION, Client.config(ctxId).binding().version());
- assertEquals(DefaultLoader.BINDING_LIBRARY_NAME, Client.config(ctxId).binding().library());
- assertEquals(DefaultLoader.BINDING_LIBRARY_VERSION, Client.config(ctxId2).binding().version());
- assertEquals(DefaultLoader.BINDING_LIBRARY_NAME, Client.config(ctxId2).binding().library());
+ assertEquals(DefaultLoader.BINDING_LIBRARY_VERSION, Client.config(ctxId).get().binding().version());
+ assertEquals(DefaultLoader.BINDING_LIBRARY_NAME, Client.config(ctxId).get().binding().library());
+ assertEquals(DefaultLoader.BINDING_LIBRARY_VERSION, Client.config(ctxId2).get().binding().version());
+ assertEquals(DefaultLoader.BINDING_LIBRARY_NAME, Client.config(ctxId2).get().binding().library());
}
}
diff --git a/src/test/java/tech/deplant/java4ever/unit/CryptoTests.java b/src/test/java/tech/deplant/java4ever/unit/CryptoTests.java
index a7705c2..0998e7c 100644
--- a/src/test/java/tech/deplant/java4ever/unit/CryptoTests.java
+++ b/src/test/java/tech/deplant/java4ever/unit/CryptoTests.java
@@ -19,6 +19,7 @@
import tech.deplant.java4ever.binding.EverSdk;
import tech.deplant.java4ever.binding.loader.AbsolutePathLoader;
+import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -34,16 +35,16 @@ public class CryptoTests {
@BeforeAll
public static void loadSdk() {
- EverSdk.load(new AbsolutePathLoader("c:/opt/sdk/ton_client.dll"));
+ TestEnv.loadEverSdk();
}
@ParameterizedTest
@ValueSource(ints = {12,24})
@OnlineMeans(url = TestEnv.NODESE_URL, connectTimeout = 500, readTimeout = 1500)
- public void mnemonic_from_random_should_pass_regexp_for_word_count(int words) throws EverSdkException {
+ public void mnemonic_from_random_should_pass_regexp_for_word_count(int words) throws EverSdkException, ExecutionException, InterruptedException {
int ctxId = TestEnv.newContext();
Pattern pattern = Pattern.compile("[\\w-]+");
- Matcher matcher = pattern.matcher(Crypto.mnemonicFromRandom(ctxId, Crypto.MnemonicDictionary.English, words).phrase());
+ Matcher matcher = pattern.matcher(Crypto.mnemonicFromRandom(ctxId, Crypto.MnemonicDictionary.English, words).get().phrase());
int count = 0;
while (matcher.find())
count++;
diff --git a/src/test/java/tech/deplant/java4ever/unit/ProcessingTests.java b/src/test/java/tech/deplant/java4ever/unit/ProcessingTests.java
new file mode 100644
index 0000000..7add2b5
--- /dev/null
+++ b/src/test/java/tech/deplant/java4ever/unit/ProcessingTests.java
@@ -0,0 +1,43 @@
+package tech.deplant.java4ever.unit;
+
+import com.yegor256.OnlineMeans;
+import com.yegor256.WeAreOnline;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayNameGeneration;
+import org.junit.jupiter.api.DisplayNameGenerator;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import tech.deplant.java4ever.binding.Client;
+import tech.deplant.java4ever.binding.EverSdk;
+import tech.deplant.java4ever.binding.EverSdkException;
+import tech.deplant.java4ever.binding.Processing;
+import tech.deplant.java4ever.binding.loader.AbsolutePathLoader;
+import tech.deplant.java4ever.binding.loader.DefaultLoader;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
+@Execution(ExecutionMode.CONCURRENT)
+@ExtendWith(WeAreOnline.class)
+public class ProcessingTests {
+
+ private static final Logger log = LoggerFactory.getLogger(ProcessingTests.class);
+
+ @BeforeAll
+ public static void loadSdk() {
+ TestEnv.loadEverSdk();
+ }
+
+ @Test
+ @OnlineMeans(url = TestEnv.NODESE_URL, connectTimeout = 500, readTimeout = 1500)
+ public void cancel_monitor_not_throws_for_random_queue() throws EverSdkException {
+ int ctxId = TestEnv.newContext();
+ assertDoesNotThrow(() -> Processing.cancelMonitor(ctxId,"aaa"));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/tech/deplant/java4ever/unit/ProofsTests.java b/src/test/java/tech/deplant/java4ever/unit/ProofsTests.java
new file mode 100644
index 0000000..fd25d16
--- /dev/null
+++ b/src/test/java/tech/deplant/java4ever/unit/ProofsTests.java
@@ -0,0 +1,40 @@
+package tech.deplant.java4ever.unit;
+
+import com.yegor256.OnlineMeans;
+import com.yegor256.WeAreOnline;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayNameGeneration;
+import org.junit.jupiter.api.DisplayNameGenerator;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import tech.deplant.java4ever.binding.EverSdk;
+import tech.deplant.java4ever.binding.EverSdkException;
+import tech.deplant.java4ever.binding.Processing;
+import tech.deplant.java4ever.binding.loader.AbsolutePathLoader;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
+@Execution(ExecutionMode.CONCURRENT)
+@ExtendWith(WeAreOnline.class)
+public class ProofsTests {
+
+ private static final Logger log = LoggerFactory.getLogger(ProofsTests.class);
+
+ @BeforeAll
+ public static void loadSdk() {
+ TestEnv.loadEverSdk();
+ }
+
+ @Test
+ @OnlineMeans(url = TestEnv.NODESE_URL, connectTimeout = 500, readTimeout = 1500)
+ public void cancel_monitor_not_throws_for_random_queue() throws EverSdkException {
+ int ctxId = TestEnv.newContext();
+ assertDoesNotThrow(() -> Processing.cancelMonitor(ctxId,"aaa"));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/tech/deplant/java4ever/unit/SubscribeTests.java b/src/test/java/tech/deplant/java4ever/unit/SubscribeTests.java
index f62d436..b4f19bc 100644
--- a/src/test/java/tech/deplant/java4ever/unit/SubscribeTests.java
+++ b/src/test/java/tech/deplant/java4ever/unit/SubscribeTests.java
@@ -15,6 +15,7 @@
import tech.deplant.java4ever.binding.loader.AbsolutePathLoader;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -26,13 +27,13 @@ public class SubscribeTests {
@BeforeAll
public static void loadSdk() {
- EverSdk.load(new AbsolutePathLoader("c:/opt/sdk/ton_client.dll"));
+ TestEnv.loadEverSdk();
}
@Test
@OnlineMeans(url = TestEnv.NODESE_URL, connectTimeout = 500, readTimeout = 1500)
- public void subscribe_to_account() throws EverSdkException {
- var configJson = STR."{\"network\":{\"endpoints\":[\"\{TestEnv.NODESE_ENDPOINT}\"]}}";
+ public void subscribe_to_account() throws EverSdkException, ExecutionException, InterruptedException {
+ //var configJson = STR."{\"network\":{\"endpoints\":[\"\{TestEnv.NODESE_ENDPOINT}\"]}}";
int ctxId = TestEnv.newContext();
String queryText = """
subscription {
@@ -50,8 +51,8 @@ public void subscribe_to_account() throws EverSdkException {
var handle = Net.subscribe(ctxId,
queryText,
JsonContext.SDK_JSON_MAPPER().valueToTree(Map.of()),
- new EverSdkSubscription(eventString -> logger.log(System.Logger.Level.WARNING,
- "code: %s".formatted(eventString))))
+ eventString -> logger.log(System.Logger.Level.WARNING,
+ "code: %s".formatted(eventString))).get()
.handle();
assertTrue(handle > 0);
diff --git a/src/test/java/tech/deplant/java4ever/unit/TestEnv.java b/src/test/java/tech/deplant/java4ever/unit/TestEnv.java
index 73d86c8..00cc94d 100644
--- a/src/test/java/tech/deplant/java4ever/unit/TestEnv.java
+++ b/src/test/java/tech/deplant/java4ever/unit/TestEnv.java
@@ -3,11 +3,28 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.jupiter.api.Test;
import tech.deplant.java4ever.binding.EverSdk;
+import tech.deplant.java4ever.binding.loader.AbsolutePathLoader;
public class TestEnv {
public static final String NODESE_URL = "https://nodese.truequery.tech";
public static final String NODESE_ENDPOINT = "https://nodese.truequery.tech/graphql";
+ static void loadEverSdk() {
+ EverSdk.load(new AbsolutePathLoader("c:/opt/sdk/ton_client.dll"));
+ }
+
+ static void loadAckiNackiSdk() {
+ EverSdk.load(new AbsolutePathLoader("c:/opt/gosh-sdk/ton_client.dll"));
+ }
+
+ static int newContextEmpty() {
+ try {
+ return EverSdk.createDefault().orElseThrow();
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
static int newContext() {
try {
return EverSdk.createWithEndpoint(NODESE_ENDPOINT).orElseThrow();