0
votes

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)

1
OK solved it. I had to encapsulate the functions within this code block extern "C" { ... } since I'm using C++ and not C (see also MySQL Reference Manual). And now it "finds the symbol"user46726

1 Answers

0
votes

mysql understands only C-like names of functions. You need to export of such names obviously:

#if defined(_WIN32) || defined(_WIN64)
#define DLLEXP extern "C" __declspec(dllexport)

And just in case try to compile your dll with flags, with which mysql was compiled on your platform. For this use mysql_config utility from mysql distribution like this:

mysql_config --cxxflags

So you will be able to define necessary compilation flags.