I have created a MySQL UDF that calls Java functions using JNI. MS Visual Studio compiles my code without any problems and generates a DLL for my 64bit MySQL Server (v.5.5).
Here is what the C/C++ code looks like:
#ifdef STANDARD
/* STANDARD is defined, don't use any mysql functions */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef __WIN__
typedef unsigned __int64 ulonglong;/* Microsofts 64 bit types */
typedef __int64 longlong;
#else
typedef unsigned long long ulonglong;
typedef long long longlong;
#endif /*__WIN__*/
#else
#include <my_global.h>
#include <my_sys.h>
#if defined(MYSQL_SERVER)
#include <m_string.h>/* To get strmov() */
#else
/* when compiled as standalone */
#include <string.h>
#define strmov(a,b) stpcpy(a,b)
#define bzero(a,b) memset(a,0,b)
#define memcpy_fixed(a,b,c) memcpy(a,b,c)
#endif
#endif
#include <mysql.h>
#include <ctype.h>
#ifdef _WIN32
/* inet_aton needs winsock library */
#pragma comment(lib, "ws2_32")
#endif
#ifdef HAVE_DLOPEN
#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST)
static pthread_mutex_t LOCK_hostname;
#endif
#if defined(_WIN32) || defined(_WIN64)
#define DLLEXP __declspec(dllexport)
#else
#define DLLEXP
#endif
#include <jni.h>
DLLEXP my_bool call_java_method_init(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, char *message);
DLLEXP void call_java_method_deinit(UDF_INIT *initid __attribute__((unused)));
DLLEXP char* call_java_method(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
char* is_null __attribute__((unused)), char* error __attribute__((unused)));
static JNIEnv *env;
static JavaVM *jvm;
JNIEnv* create_vm(JavaVM **jvm) {
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options;
options.optionString = "-Djava.class.path=C:\\JavaApps\\CallJavaFromC++\\bin"; //Path to the java source code
vm_args.version = JNI_VERSION_1_6; //JDK version. This indicates version 1.6
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args);
if(ret < 0)
printf("\nUnable to Launch JVM\n");
return env;
}
my_bool call_java_method_init(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, char *message)
{
if(!(args->arg_count == 3)) {
char *m = "Expected 3 arguments. Function usage: call_java_method('classPath', 'className', 'methodName(methodArguments)') ";
strcpy_s(message, strlen(m)+1, m);
return 1;
}
env = create_vm(&jvm);
if (env == NULL){
char *m = "Could not load JVM!";
strcpy_s(message, strlen(m)+1, m);
return 1;
}
args->arg_type[0] = STRING_RESULT;
args->arg_type[1] = STRING_RESULT;
args->arg_type[2] = STRING_RESULT;
return 0;
}
void call_java_method_deinit(UDF_INIT *initid __attribute__((unused)))
{
int n = jvm->DestroyJavaVM();
}
char* call_java_method(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
char* is_null __attribute__((unused)), char* error __attribute__((unused)))
{
char* retResult = "";
jclass clsLEC = NULL;
jmethodID callJavaClass = NULL;
jstring jstrcalljava = NULL;
const char *pResult = NULL;
clsLEC = env->FindClass("LoadExternalClass");
if(clsLEC != NULL)
{
callJavaClass = env->GetStaticMethodID(clsLEC,"callJavaClass","([Ljava/lang/String;)Ljava/lang/String;");
}
else
{
printf("\nUnable to find the requested class\n");
}
if(clsLEC != NULL && callJavaClass != NULL)
{
jobjectArray ret= (jobjectArray)env->NewObjectArray(3, env->FindClass("java/lang/String"), env->NewStringUTF(""));
env->SetObjectArrayElement(ret,0,env->NewStringUTF(args->args[0]));
env->SetObjectArrayElement(ret,1,env->NewStringUTF(args->args[1]));
env->SetObjectArrayElement(ret,2,env->NewStringUTF(args->args[2]));
jstrcalljava = (jstring)env->CallStaticObjectMethod(clsLEC,callJavaClass,ret);
pResult = env->GetStringUTFChars(jstrcalljava,0);
retResult = _strdup(pResult);
env->ReleaseStringUTFChars(jstrcalljava,pResult);
}
//Release resources.
return retResult;
}
#endif /* HAVE_DLOPEN */
I've dropped the MySQLJavaUDF.dll file into the MySQL plugins folder. In order to verify that the file is compiled for a 64bit OS I've used the PE Deconstructor programmed by a stackoverflow member (How can I test a Windows DLL file to determine if it is 32 bit or 64 bit?).
Then I also checked whether the DLL contains all required functions by executing Visual Studios dumpbin /EXPORTS command. The output looks as follows and seems to be correct:
File Type: DLL Section contains the following exports for MySQLJavaUDF.dll 00000000 characteristics 531F74A1 time date stamp Tue Mar 11 21:40:01 2014 0.00 version 1 ordinal base 3 number of functions 3 number of names ordinal hint RVA name 1 0 00001150 ?call_java_method@@YAPEADPEAUst_udf_init@@PEAUst_udf_a rgs@@PEAD2@Z = ?call_java_method@@YAPEADPEAUst_udf_init@@PEAUst_udf_args@@PEAD2@ Z (char * __cdecl call_java_method(struct st_udf_init *,struct st_udf_args *,cha r *,char *)) 2 1 00001140 ?call_java_method_deinit@@YAXPEAUst_udf_init@@@Z = ?ca ll_java_method_deinit@@YAXPEAUst_udf_init@@@Z (void __cdecl call_java_method_dei nit(struct st_udf_init *)) 3 2 000010B0 ?call_java_method_init@@YADPEAUst_udf_init@@PEAUst_udf _args@@PEAD@Z = ?call_java_method_init@@YADPEAUst_udf_init@@PEAUst_udf_args@@PEA D@Z (char __cdecl call_java_method_init(struct st_udf_init *,struct st_udf_args *,char *)) Summary 4000 .data 2000 .pdata 9000 .rdata 1000 .reloc 5000 .rsrc 22000 .text
Then I've made the first attempt to install the UDF by typing the following command in the MySQL Browser:
CREATE FUNCTION call_java_method
RETURNS STRING
SONAME 'MySQLJavaUDF.dll';
The installation failed with error 1126:
Can't open shared library 'MySQLJavaUDF.dll' (errno: 126 The specified module could not be found.)
So I've downloaded the Process Monitor to see what happens when installing the DLL file as suggested in the post Monitoring application calls to DLL
It turned out that the jvm.dll could not be found. OK, copied the file from my Java 1.6.45 64bit installation folder into the MySQL Server/bin folder, executed the SQL command again and got again a "NAME NOT FOUND" result from Procmon regarding a WINMM.dll file. No problem, copied and pasted the file in question from my Windows/System32 directory into the bin folder. This time Procmon only reported "SUCCESS" results, but now MySQL complains with error 1127, telling me it
Can't find symbol 'call_java_method' in library
.
And here is where I'm stuck and gave up. Maybe one of you experts have any ideas about what is going wrong. I suspect that the DLL cannot load the JVM for some reason. I also think that copying the jvm.dll into the bin folder is not the right approach.
My system:
Windows 7 64bit
MySQL Server 5.5 64bit
Java 1.6.45 64bit
MS Visual Studio 2012 (using x64 solution platform)