Is it possible to make a Java JNI which calls jdbc?

前端 未结 1 1334
感动是毒
感动是毒 2021-01-22 08:24

I am new to jni and very confused if I can use jni to achieve what I need done. I want to make a java api what will use jdbc to update database, but this particular api will be

1条回答
  •  粉色の甜心
    2021-01-22 08:34

    I'm not going to lecture you on the right or wrong way to approach what you are trying to do. However, if you are trying to invoke Java code (JDBC.jar) then the following is for you.. Otherwise feel free to downvote.

    JVM.hpp:

    #ifndef JVM_HPP_INCLUDED
    #define JVM_HPP_INCLUDED
    
    #include "../java/jni.h"
    #include 
    #include 
    #include 
    #include 
    
    class Jvm
    {
        private:
            JavaVM* jvm;
            JNIEnv* env;
            JavaVMInitArgs jvm_args;
            jclass systemClassLoader;
    
        public:
            Jvm(std::string ClassPath = ".");
            ~Jvm();
    
            inline JavaVM* GetJVM() const {return jvm;}
            inline JNIEnv* GetENV() const {return env;}
            inline jclass GetSystemClassLoader() const {return systemClassLoader;}
            void DestroyJVM();
            void PrintStackTrace();
            jclass DefineClass(const char* FullClassName, const void* ClassBuffer, std::uint32_t BufferLength);
            jclass DefineClass(const char* FullClassName, jobject ClassLoader, const void* ClassBuffer, std::uint32_t BufferLength);
    
            void RegisterNativeMethod(const char* MethodName, const char* MethodSignature, void* func_ptr);
            void RegisterNativeMethod(jobject ClassLoader, const char* MethodName, const char* MethodSignature, void* func_ptr);
            void RegisterNativeMethods(JNINativeMethod* Methods, std::uint32_t MethodCount);
            void RegisterNativeMethods(jobject ClassLoader, JNINativeMethod* Methods, std::uint32_t MethodCount);
    
        protected:
            void InitClassLoader();
    };
    
    #endif // JVM_HPP_INCLUDED
    

    JVM.cpp:

    #include "JVM.hpp"
    
    Jvm::~Jvm()
    {
        env->DeleteGlobalRef(this->systemClassLoader);
        jvm->DestroyJavaVM();
    }
    
    Jvm::Jvm(std::string ClassPath) : jvm(NULL), env(NULL), jvm_args(), systemClassLoader(NULL)
    {
        JavaVMOption* options = new JavaVMOption[2];
        jvm_args.version = JNI_VERSION_1_6;
        JNI_GetDefaultJavaVMInitArgs(&jvm_args);
        options[0].optionString = const_cast("-Djava.compiler=NONE");
        options[1].optionString = const_cast(("-Djava.class.path=" + ClassPath).c_str());
        jvm_args.nOptions = 2;
        jvm_args.options = options;
        jvm_args.ignoreUnrecognized = false;
    
        if (JNI_CreateJavaVM(&jvm, reinterpret_cast(&env), &jvm_args))
        {
            delete[] options;
            throw std::runtime_error("Failed To Create JVM Instance.");
        }
    
        delete[] options;
    }
    
    void Jvm::InitClassLoader()
    {
        if (!this->systemClassLoader)
        {
            jclass classloader = env->FindClass("Ljava/lang/ClassLoader;");
            if (!classloader)
            {
                throw std::runtime_error("Failed To find ClassLoader.");
            }
    
            jmethodID SystemLoaderMethod = env->GetStaticMethodID(classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
            jobject loader = env->CallStaticObjectMethod(classloader, SystemLoaderMethod);
            this->systemClassLoader = reinterpret_cast(env->NewGlobalRef(loader));
        }
    }
    
    void Jvm::PrintStackTrace()
    {
        if (env->ExceptionOccurred())
        {
            env->ExceptionDescribe();
            env->ExceptionClear();
        }
    }
    
    jclass Jvm::DefineClass(const char* FullClassName, const void* ClassBuffer, std::uint32_t BufferLength)
    {
        this->InitClassLoader();
        return this->DefineClass(FullClassName, this->systemClassLoader, ClassBuffer, BufferLength);
    }
    
    jclass Jvm::DefineClass(const char* FullClassName, jobject ClassLoader, const void* ClassBuffer, std::uint32_t BufferLength)
    {
        return ClassLoader ? env->DefineClass(FullClassName, ClassLoader, static_cast(ClassBuffer), BufferLength) : NULL;
    }
    
    void Jvm::RegisterNativeMethod(const char* MethodName, const char* MethodSignature, void* func_ptr)
    {
        JNINativeMethod method;
        method.name = const_cast(MethodName);
        method.signature = const_cast(MethodSignature);
        method.fnPtr = func_ptr;
        this->RegisterNativeMethods(&method, 1);
    }
    
    void Jvm::RegisterNativeMethod(jobject ClassLoader, const char* MethodName, const char* MethodSignature, void* func_ptr)
    {
        JNINativeMethod method;
        method.name = const_cast(MethodName);
        method.signature = const_cast(MethodSignature);
        method.fnPtr = func_ptr;
        this->RegisterNativeMethods(ClassLoader, &method, 1);
    }
    
    void Jvm::RegisterNativeMethods(JNINativeMethod* Methods, std::uint32_t MethodCount)
    {
        this->InitClassLoader();
        this->RegisterNativeMethods(this->systemClassLoader, Methods, MethodCount);
    }
    
    void Jvm::RegisterNativeMethods(jobject ClassLoader, JNINativeMethod* Methods, std::uint32_t MethodCount)
    {
        if (ClassLoader)
        {
            env->RegisterNatives(static_cast(ClassLoader), Methods, MethodCount);
        }
    }
    

    You can then create an instance of it that loads your jar.

    int main()
    {
        Jvm VM("C:/Users/Brandon/IdeaProjects/Eos/out/production/Eos/Bot.jar");
    
        jclass jMain = VM.GetENV()->FindClass("eos/Main");
    
        if (jMain != nullptr)
        {
            jmethodID mainMethod = env->GetStaticMethodID(jMain, "main", "([Ljava/lang/String;)V");
            jclass StringClass = env->FindClass("java/lang/String");
            jobjectArray Args = env->NewObjectArray(0, StringClass, 0);
            env->CallStaticVoidMethod(jMain, MainMethod, Args);
        }
    }
    

    Now this just shows how to run a jar with a Main Method. However, you can access ANY class from the jar and invoke it with however many parameters needed. It doesn't need a main.

    Now it IS a lot MORE work to do this, but I won't lecture you. The question was whether or not is was possible and the answer is YES.. it is. So long as you create an instance of the "JVM". After that, it's a matter of accessing the class via the "Package/Class" NOT "Package.Class" as done in Java.. then invoking whatever methods you want.

    0 讨论(0)
提交回复
热议问题