All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.javonet.sdk.RuntimeContext Maven / Gradle / Ivy

Go to download

Javonet allows you to reference and use modules or packages written in (Java/Kotlin/Groovy/Clojure, C#/VB.NET, Ruby, Perl, Python, JavaScript/TypeScript) like they were created in your technology. It works on Linux/Windows and MacOS for applications created in JVM, CLR/Netcore, Perl, Python, Ruby, NodeJS, C++ or GoLang and gives you unparalleled freedom and flexibility with native performance in building your mixed-technologies products. Let it be accessing best AI or cryptography libraries, devices SDKs, legacy client modules, internal custom packages or anything from public repositories available on NPM, Nuget, PyPI, Maven/Gradle, RubyGems or GitHub. Get free from programming languages barriers today! For more information check out our guides at https://www.javonet.com/guides/v2/

There is a newer version: 2.4.5
Show newest version
package com.javonet.sdk;

import com.javonet.core.generator.handler.GeneratorHandler;
import com.javonet.core.handler.Handler;
import com.javonet.core.interpreter.Interpreter;
import com.javonet.sdk.internal.AbstractModuleContext;
import com.javonet.sdk.internal.AbstractRuntimeContext;
import com.javonet.sdk.internal.AbstractTypeContext;
import com.javonet.utils.*;
import com.javonet.utils.exceptions.ExceptionThrower;

import java.util.Arrays;
import java.util.HashMap;

/**
 * Represents a single context which allows interaction with a selected technology.
 * Refers to a single instance of the called runtime within a particular target OS process.
 * This can be either the local currently running process (inMemory) or a particular remote process identified by the IP Address and PORT of the target Javonet instance.
 * Multiple Runtime Contexts can be initialized within one process.
 * Calling the same technology on inMemory communication channel will return the existing instance of runtime context.
 * Calling the same technology on TCP channel but on different nodes will result in unique Runtime Contexts.
 * Within the runtime context, any number of libraries can be loaded and any objects from the target technology can be interacted with, as they are aware of each other due to sharing the same memory space and same runtime instance.
 *
 * @see Runtime Context Guide
 */
public class RuntimeContext implements AbstractTypeContext, AbstractModuleContext, AbstractRuntimeContext {

    private final RuntimeName runtimeName;
    private final ConnectionType connectionType;
    private final TcpConnectionData tcpConnectionData;

    private Command currentCommand;
    private Command responseCommand;
    private final Handler handler = new Handler();
    private final Interpreter interpreter = new Interpreter();

    private static final HashMap memoryRuntimeContexts = new HashMap<>();
    private static final HashMap networkRuntimeContexts = new HashMap<>();
    private final GeneratorHandler generatorHandler = new GeneratorHandler();

    /**
     * Represents a single context which allows interaction with a selected technology.
     * Refers to a single instance of the called runtime within a particular target OS process.
     * This can be either the local currently running process (inMemory) or a particular remote process identified by the IP Address and PORT of the target Javonet instance.
     * Multiple Runtime Contexts can be initialized within one process.
     * Calling the same technology on inMemory communication channel will return the existing instance of runtime context.
     * Calling the same technology on TCP channel but on different nodes will result in unique Runtime Contexts.
     * Within the runtime context, any number of libraries can be loaded and any objects from the target technology can be interacted with, as they are aware of each other due to sharing the same memory space and same runtime instance.
     *
     * @param runtimeName       The name of the runtime.
     * @param connectionType    The type of the connection.
     * @param tcpConnectionData The IP Address and PORT of the target Javonet instance.
     * @return RuntimeContext instance.
     * @see Article on Javonet Guides 
     */
    public static RuntimeContext getInstance(RuntimeName runtimeName, ConnectionType connectionType, TcpConnectionData tcpConnectionData) {
        switch (connectionType) {
            case IN_MEMORY:
                if (memoryRuntimeContexts.containsKey(runtimeName)) {
                    RuntimeContext runtimeContext = memoryRuntimeContexts.get(runtimeName);
                    runtimeContext.currentCommand = null;
                    return runtimeContext;
                } else {
                    RuntimeContext currentContext = new RuntimeContext(runtimeName, connectionType, tcpConnectionData);
                    memoryRuntimeContexts.put(runtimeName, currentContext);
                    return currentContext;
                }
            case TCP:
                String networkRuntimeContextsKey = runtimeName + ":" + tcpConnectionData.toString();
                if (networkRuntimeContexts.containsKey(networkRuntimeContextsKey)) {
                    RuntimeContext runtimeContext = networkRuntimeContexts.get(networkRuntimeContextsKey);
                    runtimeContext.currentCommand = null;
                    return runtimeContext;
                } else {
                    RuntimeContext currentContext = new RuntimeContext(runtimeName, connectionType, tcpConnectionData);
                    networkRuntimeContexts.put(networkRuntimeContextsKey, currentContext);
                    return currentContext;
                }
            default:
                throw new RuntimeException("Unknown connection type: " + connectionType);
        }
    }

    private RuntimeContext(RuntimeName runtimeName, ConnectionType connectionType, TcpConnectionData tcpConnectionData) {
        this.runtimeName = runtimeName;
        this.connectionType = connectionType;
        this.tcpConnectionData = tcpConnectionData;
        if (RuntimeName.Jvm != runtimeName) {
            BinariesUnloader.extractBinariesFromJar(runtimeName);
        }
    }

    /**
     * Executes the current command. The initial state of RuntimeContext is non-materialized,
     * wrapping either a single command or a chain of recursively nested commands.
     * Commands become nested through each invocation of methods on RuntimeContext.
     * Each invocation triggers the creation of a new RuntimeContext instance wrapping the current command with a new parent command.
     * The developer can decide at any moment of the materialization for the context, taking full control of the chunks of the expression being transferred and processed on the target runtime.
     *
     * @see Article on Javonet Guides
     */
    public void execute() {
        this.responseCommand = this.interpreter.execute(currentCommand, connectionType, tcpConnectionData);
        this.currentCommand = null;

        if (this.responseCommand.getCommandType() == CommandType.EXCEPTION) {
            ExceptionThrower.throwException(this.responseCommand);
        }
    }

    /**
     * Adds a reference to a library. Javonet allows you to reference and use modules or packages written in various languages.
     * This method allows you to use any library from all supported technologies. The necessary libraries need to be referenced.
     * The argument is a relative or full path to the library. If the library has dependencies on other libraries, the latter needs to be added first.
     * After referencing the library, any objects stored in this package can be used. Use static classes, create instances, call methods, use fields and properties, and much more.
     *
     * @param libraryPath The relative or full path to the library.
     * @return RuntimeContext instance.
     * @see Article on Javonet Guides
     */
    public RuntimeContext loadLibrary(String libraryPath) {
        Command localCommand = new Command(this.runtimeName, CommandType.LOAD_LIBRARY, libraryPath);
        currentCommand = buildCommand(localCommand);
        execute();
        return this;
    }

    /**
     * Retrieves a reference to a specific type. The type can be a class, interface or enum. The type can be retrieved from any referenced library.
     *
     * @param args The optional generic type arguments.
     * @return InvocationContext instance that wraps the command to retrieve a reference to a specific type.
     */
    @Override
    public InvocationContext getType(String typeName, Object... args) {
        Command localCommand = new Command(this.runtimeName, CommandType.GET_TYPE, createArgsArray(typeName, args));
        currentCommand = null;
        return new InvocationContext(this.runtimeName, this.connectionType, this.tcpConnectionData, buildCommand(localCommand));
    }

    /**
     * Casts the provided value to a specific type. This method is used when invoking methods that require specific types of arguments.
     * The arguments include the target type and the value to be cast. The target type must be retrieved from the called runtime using the getType method.
     * After casting the value, it can be used as an argument when invoking methods.
     *
     * @param args The target type and the value to be cast.
     * @return InvocationContext instance that wraps the command to cast the provided value to a specific type.
     * @see Article on Javonet Guides
     */
    public InvocationContext cast(Object... args) {
        Command localCommand = new Command(this.runtimeName, CommandType.CAST, args);
        currentCommand = null;
        return new InvocationContext(this.runtimeName, this.connectionType, this.tcpConnectionData, buildCommand(localCommand));
    }

    public InvocationContext invokeGlobalMethod(Object... args) {
        Command localCommand = new Command(this.runtimeName, CommandType.INVOKE_GLOBAL_METHOD, args);
        return new InvocationContext(this.runtimeName, this.connectionType, this.tcpConnectionData, buildCommand(localCommand));
    }

    /**
     * Generates a library based on the provided arguments. This method is used to create a library that can be used in the runtime.
     * The arguments include the necessary information for generating the library.
     * This method analyzes the library file, generate the necessary class files, compile them, and load the classes.
     * After generating the library, it can be used in the runtime.
     *
     * @param args The path for generating the library.
     * @return RuntimeContext instance that wraps the command to generate a library.
     */
    public RuntimeContext generateLibrary(Object... args) {
        Command localCommand = new Command(this.runtimeName, CommandType.GENERATE_LIB, args);
        currentCommand = buildCommand(localCommand);
        execute();
        generatorHandler.generate(this.responseCommand, System.getProperty("user.dir"));
        return this;
    }

    /**
     * Retrieves a specific item from an enum type. This method is used when working with enums from the called runtime.
     * The arguments include the enum type and the name of the item. The enum type must be retrieved from the called runtime using the getType method.
     * After retrieving the item, it can be used as an argument when invoking methods or for other operations.
     *
     * @param args The enum type and the name of the item.
     * @return InvocationContext instance that wraps the command to retrieve a specific item from an enum type.
     * @see Article on Javonet Guides
     */
    public InvocationContext getEnumItem(Object... args) {
        Command localCommand = new Command(this.runtimeName, CommandType.GET_ENUM_ITEM, args);
        currentCommand = null;
        return new InvocationContext(this.runtimeName, this.connectionType, this.tcpConnectionData, buildCommand(localCommand));
    }

    /**
     * Creates a reference type argument that can be passed to a method with an out parameter modifier. This method is used when working with methods from the called runtime that require arguments to be passed by reference.
     * The arguments include the value and optionally the type of the reference. The type must be retrieved from the called runtime using the getType method.
     * After creating the reference, it can be used as an argument when invoking methods.
     *
     * @param args The value and optionally the type of the reference.
     * @return InvocationContext instance that wraps the command to create a reference type argument.
     * @see Article on Javonet Guides
     */
    public InvocationContext asOut(Object... args) {
        Command localCommand = new Command(this.runtimeName, CommandType.AS_OUT, args);
        currentCommand = null;
        return new InvocationContext(this.runtimeName, this.connectionType, this.tcpConnectionData, buildCommand(localCommand));
    }

    /**
     * Creates a reference type argument that can be passed to a method with a ref parameter modifier. This method is used when working with methods from the called runtime that require arguments to be passed by reference.
     * The arguments include the value and optionally the type of the reference. The type must be retrieved from the called runtime using the getType method.
     * After creating the reference, it can be used as an argument when invoking methods.
     *
     * @param args The value and optionally the type of the reference.
     * @return InvocationContext instance that wraps the command to create a reference type argument.
     * @see Article on Javonet Guides
     */
    public InvocationContext asRef(Object... args) {
        Command localCommand = new Command(this.runtimeName, CommandType.AS_REF, args);
        currentCommand = null;
        return new InvocationContext(this.runtimeName, this.connectionType, this.tcpConnectionData, buildCommand(localCommand));
    }

    private Command buildCommand(Command command) {
        for (int i = 0; i < command.getPayload().length; i++) {

            command.setPayload(encapsulatePayloadItem(command.getPayload()[i]), i);
        }
        return command.prependArgumentToPayload(this.currentCommand);
    }

    private Command encapsulatePayloadItem(Object payloadItem) {
        if (payloadItem instanceof Command) {
            for (int i = 0; i < (((Command) payloadItem).getPayload()).length; i++) {
                ((Command) payloadItem).setPayload(encapsulatePayloadItem(((Command) payloadItem).getPayload()[i]), i);
            }
            return (Command) payloadItem;

        } else if (payloadItem instanceof InvocationContext) {
            return ((InvocationContext) payloadItem).getCurrentCommand();

        } else if (payloadItem.getClass().isArray()) {
            Object[] objectArray = new Object[((Object[]) payloadItem).length];
            for (int i = 0; i < ((Object[]) payloadItem).length; i++) {
                objectArray[i] = encapsulatePayloadItem(((Object[]) payloadItem)[i]);
            }
            return new Command(this.runtimeName, CommandType.ARRAY, objectArray);
        } else {
            return new Command(this.runtimeName, CommandType.VALUE, payloadItem);
        }
    }

    private Object[] createArgsArray(Object arg1, Object... args) {
        Object[] argsArray = Arrays.copyOf(new Object[]{arg1}, args.length + 1);
        System.arraycopy(args, 0, argsArray, 1, args.length);
        return argsArray;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy