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 called from C++ program.
So I think I probably should write jni code which access the database via jdbc (is that even possible?), create C++ code and generate dll so other C++ programs can just call the dll to update database. Is this all possible? If so, how do I really call jdbc in jni?
If this dll is finally made, can Fortran call it as well?
My other thought is maybe I should make a regular java program to update the database, then use say ikvm to wrap the java class into C++ dll?
The thing is I have to use access database using Java. Our C++ programs will not access database at all, and it would be better if this java api can be accessed via system call.
Or is there any better way to do it?
I hope I explained it well. I am not all familiar with what I am assigned here and cannot find much relevant reference.
Thank you so much!!
UPDATED:
The problem is not all computers have C++ postgresql driver installed but they do have Java postgresql driver installed. We don't want to force everyone to install the C++ db driver and no major changes in those C++ program will be made. So it will make sense to come up something in Java to access database. The java system service (preferred, like dll?) /API basically is called to record start time and end time of a C++ program. C++ program will make a "function" call (with pass-in parameter and returned value) to this system service/Java API to record start/end time.
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 <windows.h>
#include <iostream>
#include <stdexcept>
#include <algorithm>
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<char*>("-Djava.compiler=NONE");
options[1].optionString = const_cast<char*>(("-Djava.class.path=" + ClassPath).c_str());
jvm_args.nOptions = 2;
jvm_args.options = options;
jvm_args.ignoreUnrecognized = false;
if (JNI_CreateJavaVM(&jvm, reinterpret_cast<void**>(&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<jclass>(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<const jbyte*>(ClassBuffer), BufferLength) : NULL;
}
void Jvm::RegisterNativeMethod(const char* MethodName, const char* MethodSignature, void* func_ptr)
{
JNINativeMethod method;
method.name = const_cast<char*>(MethodName);
method.signature = const_cast<char*>(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<char*>(MethodName);
method.signature = const_cast<char*>(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<jclass>(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.
Related
I am designing an application through JNI in order to prevent third party edits. I have moved forth towards registering native methods in order to remove jni bridge linkages, but as you can see, methods with override attribute still need to exist within the java code as native linked. Is there a way to fully port the remaining java code for this specific file?
Java:
#Override
protected native void onCreate(Bundle savedInstanceState);
CPP:
void onCreate(JNIEnv *env, jobject classObject, jobject bundle) {
/**super.onCreate**/
gObjects[0] = env->NewGlobalRef(classObject);
gClasses[0] = env->GetObjectClass(gObjects[0]);
jclass s_Class = env->GetSuperclass(gClasses[0]);
jmethodID oc_ID = env->GetMethodID(s_Class, "onCreate", "(Landroid/os/Bundle;)V");
env->CallNonvirtualVoidMethod(gObjects[0], gClasses[0], oc_ID, bundle);
}
extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *curVM_, void *reserved) {
curVM = curVM_;
curVM->GetEnv((void **) &environments[0], JNI_VERSION_1_6);
/**Start of Native Method Registers**/
JNINativeMethod natives[1];
uint64_t pCount = sizeof(natives) / sizeof(natives[0]);
jclass klass = environments[0]->FindClass("com/company/app/Activity");
natives[0] = {"onCreate", "(Landroid/os/Bundle;)V", (void *)onCreate};
environments[0]->RegisterNatives(klass , natives, pCount);\
for (uint64_t i = 0; i < pCount; i++) natives[i] = {"", ""};\
return JNI_VERSION_1_6;
}
It does seem quite silly to think I can simply remove the native linkage between the activity and lib. Instead, I will be utilizing BaseDexClassLoader to sideload my dex file during runtime. I will not be giving up my code, but further information about this can be founded at: https://developer.android.com/reference/dalvik/system/BaseDexClassLoader
Our Spring boot application hanged on production Unix environment. After getting the thread dump we were able to find that getSpace method of UnixFileSystem was locked. Can anyone help me with the Unix command that is called when java calls the getSpace method.
http-nio-8080-exec-1" - Thread t#45
java.lang.Thread.State: RUNNABLE
at java.io.UnixFileSystem.getSpace(Native Method)
at java.io.File.getUsableSpace(File.java:1885)
Basically a native call is made in java.io.UnixFileSystem to getSpace(...):
/* -- Disk usage -- */
public native long getSpace(File f, int t);
This native method ends up calling via JNI the method defined in this file of the JDK's repository:
https://github.com/openjdk/jdk/blob/master/src/java.base/unix/native/libjava/UnixFileSystem_md.c
In this file, at line 466, you find the following implementation (parts inside the if/else statements have been omitted for brevity):
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
jobject file, jint t)
{
jlong rv = 0L;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
#ifdef MACOSX
struct statfs fsstat;
#else
struct statvfs64 fsstat;
int res;
#endif
memset(&fsstat, 0, sizeof(fsstat));
#ifdef MACOSX
if (statfs(path, &fsstat) == 0) {
switch(t) {
// omitted
}
}
#else
RESTARTABLE(statvfs64(path, &fsstat), res);
if (res == 0) {
switch(t) {
// omitted
}
}
#endif
} END_PLATFORM_STRING(env, path);
return rv;
}
So as you can see, if you are using MACOSX the C library function statfs is called, otherwise the function statvfs64 is called.
I have error while trying invoke java method from native code.
[arm64-v8a] Compile++ : hell <= hell.cpp
/home/zns/AndroidStudioProjects/Test/app/src/main/jni/hell.cpp: In function 'int main()':
/home/zns/AndroidStudioProjects/Test/app/src/main/jni/hell.cpp:8:42: error: 'JNI_CreateJavaVM' was not declared in this scope
JNI_CreateJavaVM(&jvm, &env, &vm_args);
^
make: *** [/home/zns/AndroidStudioProjects/Test/app/src/main/obj/local/arm64-v8a/objs/hell/hell.o] Error 1
hell.cpp
#include <string.h>
#include <jni.h>
int main(){
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
vm_args.version = JNI_VERSION_1_6;
JNI_CreateJavaVM(&jvm, &env, &vm_args);
jclass cls = env->FindClass("MainActivity");
jmethodID mid = env->GetStaticMethodID(cls, "test", "()V");
env->CallStaticVoidMethod(cls, mid, 100);
jvm->DestroyJavaVM();
}
extern "C" {
jstring
Java_com_oxide_app_MainActivity_stringFromJNI
(JNIEnv *env, jobject obj)
{
main();
return env->NewStringUTF("Hello from C++ over JNI!");
}
}
MainActivity.java
public class MainActivity extends ActionBarActivity {
static{
System.loadLibrary("hell");
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = new TextView(this);
tv.setText(stringFromJNI());
setContentView(tv);
}
public native String stringFromJNI();
public void test(){
Log.d("NATIVE", "WHOA");
}
}
OS: linux;
jdk:/opt/icedtea-bin-6.1.12.7/;
P.S.
I have seen two similar questions, but they did not help to solve the problem
Calling a JAVA method from C++ with JNI, no parameters
Using JNI to execute a java jar from a C++ program, using g++ or eclipse
From the NDK's jni.h
#if 0 /* In practice, these are not exported by the NDK so don't declare them */
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
#endif
As the only supported way to use the NDK is from a Java application so the Java JM is already loaded.
I think you should remove your main function and look into replacing it with JNI_OnLoad and remove the calls to control the VM's lifetime.
I start JVM from C++ program.
C++ code:
JNIEXPORT jobject JNICALL com_javelin_JavelinMarketData_callBackIntoNative(JNIEnv* env, jobject obj, jlong ptr)
{
std::cout << "com_javelin_JavelinMarketData_callBackIntoNative called" << std::endl;
}
int main()
{
JavaVM* jvm;
JNIEnv* env;
...
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
jmethodID mainMethod = env->GetStaticMethodID(helloWorldClass, "main", "([Ljava/lang/String;)V");
env->CallStaticVoidMethod(helloWorldClass, mainMethod, ...);
}
then I need my C++ function called back from java:
Java code:
native void callBackIntoNative(long ptr);
public void onResponse(long param)
{
System.out.println("Calling callBackIntoNative()");
callBackIntoNative(param);
}
When I run my C++ program, JVM starts correctly, but after it prints "Calling callBackIntoNative()" the following acception appears:
Exception during callBackIntoNative(): 'com.javelin.JavelinMarketData.callBackIntoNative(J)V'
java.lang.UnsatisfiedLinkError: com.javelin.JavelinMarketData.callBackIntoNative(J)V
Can you please help?
Thanks, but I found a solution: I should have registered my C++ function as a native method using the code:
const JNINativeMethod methods[] = { { "callBackIntoNative", "(J)V", (void*)&com_javelin_JavelinMarketData_callBackIntoNative } };
const int methods_size = sizeof(methods) / sizeof(methods[0]);
jclass jClass = env->FindClass("com/javelin/JavelinMarketData");
env->RegisterNatives(jClass, methods, methods_size);
Now it works fine.
I would compile your callback apart into a dynamic library (.dll, .so, whatever your OS) and put it accesible to your java program. Then you just load your library by using JNI and call from your java classes whatever functionality you have in the library.
I found these in open JDK (System.c file)
static JNINativeMethod methods[] = {
{"currentTimeMillis", "()J", (void *)&JVM_CurrentTimeMillis},
{"nanoTime", "()J", (void *)&JVM_NanoTime},
{"arraycopy", "(" OBJ "I" OBJ "II)V", (void *)&JVM_ArrayCopy},
};
#undef OBJ
JNIEXPORT void JNICALL
Java_java_lang_System_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
}
but I was not able to find the native implemetations of these functions
currentTimeMillis
nanoTime
arraycopy
Form where can I get the native implementations of these functions ?
Is that available in open JDK?
if found it in
jdk7/hotspot/src/share/vm/prims/jvm.cpp:229
JVM_LEAF(jlong, JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored))
JVMWrapper("JVM_CurrentTimeMillis");
return os::javaTimeMillis();
JVM_END
the real implementation (for linux) is in
/jdk7/hotspot/src/os/linux/vm/os_linux.cpp
the other methods are just below it