JNI in Oracle

JNI – Java Native Interface – is an interface for cooperating Java and legacy code. Oracle RDBMS doesn’t support it officially in embedded Oracle JVM (aka “Aurora”):

Oracle Database does not support the use of JNI in Java applications. If you use JNI, then your application is not 100 percent pure Java and the native methods require porting between platforms. Native methods can cause server failure, violate security, and corrupt data.

All true – and nevertheless it’s working.

      

Here is an example for standalone Java. I’ve made the same for Oracle JVM.      

  • Compiling Java class, generating header file:
public class testJNI {
    static {
        System.loadLibrary("testjni");
    }

    public static native String nativeCall();

    public static void print() {
        String str = nativeCall();
        System.out.println(str);
    }
}
[oracle@localhost ~]$ javac testJNI.java
[oracle@localhost ~]$ javah -jni testJNI
[oracle@localhost ~]$ cat testJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class testJNI */

#ifndef _Included_testJNI
#define _Included_testJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     testJNI
 * Method:    nativeCall
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_testJNI_nativeCall
  (JNIEnv *, jclass);
 

#ifdef __cplusplus
}
#endif
#endif
  • Writing function implementation, building library:
[oracle@localhost ~]$ cat testjni.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <testJNI.h>
 

JNIEXPORT jstring JNICALL Java_testJNI_nativeCall(JNIEnv *env, jclass clazz)
{
  jstring ret = 0;
  char* tmpstring = "Test program of JNI.\n";
  ret = (*env)->NewStringUTF(env, tmpstring);
  return ret;
}
[oracle@localhost ~]$ gcc -shared -fpic -o libtestjni.so -I /u01/jdk1.5.0_22/include -I /u01/jdk1.5.0_22/include/linux testjni.c
  • Loading class file into database, setting LD_LIBRARY_PATH (for connections via listener you should specify it with ENVS in listener.ora)
[oracle@localhost ~]$ loadjava -user tim/t testJNI.class
[oracle@localhost ~]$ export LD_LIBRARY_PATH=/home/oracle
  • Writing PL/SQL procedure wrapper for testJNI.print and trying to invoke it:
[oracle@localhost ~]$ sqlplus tim/t

SQL*Plus: Release 11.2.0.1.0 Production on Sun Dec 13 20:14:27 2009

Copyright (c) 1982, 2009, Oracle.  All rights reserved.
 

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
With the Partitioning option

SQL> create or replace procedure test_jni as
  2  language java name 'testJNI.print()';
  3  /

Procedure created.

SQL> set serveroutput on
SQL> exec dbms_java.set_output(10000);

PL/SQL procedure successfully completed.

SQL> exec test_jni
Exception in thread "Root Thread" java.lang.ExceptionInInitializerError
Caused by: java.security.AccessControlException: the Permission
(java.lang.RuntimePermission loadLibrary.testjni) has not been granted to TIM.
The PL/SQL to grant this is dbms_java.grant_permission( 'TIM',
'SYS:java.lang.RuntimePermission', 'loadLibrary.testjni', '' )
        at
java.security.AccessControlContext.checkPermission(AccessControlContext.java)
        at java.security.AccessController.checkPermission(AccessController.java)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java)
        at
oracle.aurora.rdbms.SecurityManagerImpl.checkPermission(SecurityManagerImpl.java
)
        at java.lang.SecurityManager.checkLink(SecurityManager.java:820)
        at java.lang.Runtime.loadLibrary0(Runtime.java:816)
        at java.lang.System.loadLibrary(System.java:1167)
        at testJNI.<clinit>(testJNI.java:3)
BEGIN test_jni; END;

*
ERROR at line 1:
ORA-29532: Java call terminated by uncaught Java exception:
java.lang.ExceptionInInitializerError
ORA-06512: at "TIM.TEST_JNI", line 1
ORA-06512: at line 1

Exception says current user don’t have permission to load library; when you try to execute the provided statement under SYS it’ll fail:

SQL> conn / as sysdba
Connected.
SQL> exec dbms_java.grant_permission( 'TIM','SYS:java.lang.RuntimePermission', 'loadLibrary.testjni', '' )
Exception in thread "Root Thread" java.lang.SecurityException: policy table update SYS:java.lang.RuntimePermission, loadLibrary.testjni
        at oracle.aurora.rdbms.security.PolicyTableManager.checkPermission(PolicyTableManager.java)
        at oracle.aurora.rdbms.security.PolicyTableManager.activate(PolicyTableManager.java)
        at oracle.aurora.rdbms.security.PolicyTableManager.grant(PolicyTableManager.java)
BEGIN dbms_java.grant_permission( 'TIM','SYS:java.lang.RuntimePermission', 'loadLibrary.testjni', '' ); END;
*
ERROR at line 1:
ORA-29532: Java call terminated by uncaught Java exception: java.lang.SecurityException: policy table update SYS:java.lang.RuntimePermission,
loadLibrary.testjni
ORA-06512: at "SYS.DBMS_JAVA", line 793
ORA-06512: at line 1

By default even SYS can not modify Java RuntimePermission of loading a library:

SQL> select seq, kind, grantee, name, enabled from dba_java_policy where name like '%java.lang.RuntimePermission%';
       SEQ KIND     GRANTEE                        NAME                                                         ENABLED
---------- -------- ------------------------------ ------------------------------------------------------------ --------
        11 GRANT    JAVA_ADMIN                     0:java.lang.RuntimePermission#*                              ENABLED
        95 RESTRICT PUBLIC                         0:java.lang.RuntimePermission#loadLibrary.*                  ENABLED

and you need to disable it explicitly:

SQL> exec dbms_java.disable_permission(95);

PL/SQL procedure successfully completed.

SQL> exec dbms_java.grant_permission( 'TIM', 'SYS:java.lang.RuntimePermission', 'loadLibrary.testjni', '' )

PL/SQL procedure successfully completed.

SQL> exec dbms_java.enable_permission(95);

PL/SQL procedure successfully completed.
  • Finally – invoke PL/SQL wrapper (which calls static Java method, which calls native C procedure…)
SQL> conn tim/t
Connected.
SQL> exec test_jni
Test program of JNI.
 
PL/SQL procedure successfully completed.

And once again: all that stuff is not supported by Oracle.

About these ads

9 Comments

  1. Mary
    Posted September 21, 2010 at 00:57 | Permalink | Reply

    SQL> exec test_jni
    BEGIN test_jni; END;

    *
    ERROR at line 1:
    ORA-29532: Java call terminated by uncaught Java exception:
    java.lang.UnsatisfiedLinkError: no testjni in java.library.path
    ORA-06512: at “SYSTEM.TEST_JNI”, line 1
    ORA-06512: at line 1

    I’m getting the above error when I execute test_jni. Any ideas?

  2. Mary
    Posted September 21, 2010 at 04:01 | Permalink | Reply

    Solved the above pbm, now getting this

    ORA-29532: Java call terminated by uncaught Java exception:
    java.lang.NoClassDefFoundError
    ORA-06512: at “SYSTEM.TEST_JNI”, line 1
    ORA-06512: at line 1

    • Posted September 21, 2010 at 10:35 | Permalink | Reply

      Hello Mary,

      the error says JVM couldn’t find Java class you are calling via PL/SQL wrapper. Did you load it properly?

      • Mary
        Posted September 21, 2010 at 20:20 | Permalink | Reply

        Thanks for the response. I followed exactly the same steps outlined in your article. Yes I load the Java class using loadjava command. When I give “select name, source from user_java_classes” I can see my java class loaded in sql-plus. When I try a simple HelloWorld.java program without any JNI calls it is working well. I don’t know where I am wrong.

        I am trying this out almost a day now.I am getting theerror “java.lang.UnsatisfiedLinkError: no testjni in java.library.path” for the first time when I execute the pl/sql procedure (exec “test_jni” )then when I try the next time I am getting the “java.lang.NoClassDefFoundError” Error

        Thanks again,
        Mary

  3. Mary
    Posted September 21, 2010 at 21:38 | Permalink | Reply

    I think the problem occurs the System.loadLibrary(“testjni”) call occurs. The libtestjni.so library is currently in my $HOME and my .bash_profile looks like the following:

    ORACLE_HOME=/opt/oracle/product/11.2.0/dbhome_1
    ORACLE_SID=orcl
    PATH=$HOME:/usr/java/jdk1.5.0_22/bin:$PATH:$ORACLE_HOME/bin
    LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME:/usr/java/jdk1.5.0_22/lib:$ORACLE_HOME/lib
    LD_LIBRARYN32_PATH=$HOME

    How do I change this to make the pl/sql procedure to locate the testjni library file.

    thanks
    Mary

  4. Mary
    Posted September 22, 2010 at 04:04 | Permalink | Reply

    I’m not getting any output from the following .class file
    public class Hello
    {
    public static String world()
    {
    String path = System.getProperty(“java.library.path”);
    return(path);
    }
    }
    if it has System.getProperty().
    Any ideas?

    • Posted September 23, 2010 at 22:58 | Permalink | Reply

      Mary,

      if you need some help, you should be prepared and make some efforts to correctly present your question – otherwise answers will include speculations, and I usually don’t like it. Here is how you can get java.library.path:

      SQL> create or replace function hello_fnc(p_prop varchar2) return varchar2 as
        2  language java name 'java.lang.System.getProperty(java.lang.String) return java.lang.String';
        3  /
      
      SQL> select hello_fnc('java.library.path') from dual;
      
      HELLO_FNC('JAVA.LIBRARY.PATH')
      ------------------------------------------------------
      :/u01/app/oracle/product/10.2/lib
      
  5. Posted October 13, 2010 at 22:04 | Permalink | Reply

    Here is a link to a thread on SQL.ru forum where someone successfully tried to invoke SAP Java Connector with a JNI library sapjco3.dll from Oracle.

    • Thomas Begert
      Posted October 14, 2010 at 10:40 | Permalink | Reply

      Hi.
      I also successfully connect directly from oracle to SAP with the sap jco2.
      I don´t need a RMI Server like descriped in Kuassi Mensahs book.
      I will test ne next days and I will write a todo.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 290 other followers