I try to write an Wrapper function in PL/SQl to call the encode function of the org.apache.commons.codec.language.colognephonetic Class, as described on official apache commons wiki. Cologne Phonetic Apache
Wrapper function looks like following:
function get_phonetic_string(i_string VARCHAR2) RETURN VARCHAR2 AS LANGUAGE java name 'org.apache.commons.codec.language.ColognePhonetic.encode(
java.lang.String
) return java.lang.String';
But, when I execute my wrapper function, I got following error: ORA-29531: no method encode in class org/apache/commons/codec/language/ColognePhonetic. But obviously there is a encode function.
Could someone help me to figure out what I'm doing wrong?
First, make sure the org.apache.commons.codec.language.ColognePhonetic class is in the database (it probably will not be).
SELECT *
FROM ALL_OBJECTS
WHERE OBJECT_TYPE LIKE '%JAVA%'
AND LOWER( OBJECT_NAME ) LIKE '%colognephonetic%';
Should return a row if it exists (may need to be run as a privileged user).
If it does not exist then you will need to use the loadjava application to load the jar library containing the classes.
Then write a wrapper to create a static function that makes an instance of the class (untested):
CREATE JAVA SOURCE NAMED Phonetics AS
import org.apache.commons.codec.language.ColognePhonetic;
public class Phonetics {
public static String encode(
final String text
){
final ColognePhonetic cp = new ColognePhonetic();
return cp.encode( text );
}
}
/
CREATE FUNCTION get_phonetic_string(i_string VARCHAR2) RETURN VARCHAR2 AS
LANGUAGE JAVA NAME 'Phonetics.encode( java.lang.String ) return java.lang.String';
Related
I am wondering if using loadjava to load the Java package called JSch.jar in an Oracle database and then loading another .java file, that utilizes the JSch package to connect over SSH, would be able to be executed within an Oracle database through a function or procedure.
I ask this before trying because I need to reach out to a DBA to try and load everything. I want to make sure it is doable because I am not very skilled in java as of yet and wouldn't know if something was impossible or if it just needs fixed.
Thanks.
Yes
Use something like:
loadjava -user USERNAME/PASSWORD#SID JSch.jar
Then create a static class method which uses the classes loaded from the Jar file:
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED MyJavaSource AS
import org.millea9805.jsch.JSchSomething;
public class MyClass {
public static String function_name()
{
JSchSomething.doSomething();
return "Something";
}
}
/
Then you can create a PL/SQL wrapper around the static Java method:
CREATE OR REPLACE FUNCTION DO_SOMETHING()
RETURN VARCHAR2
AS LANGUAGE JAVA
NAME 'MyClass.function_name() return java.lang.String';
/
A more detailed example using the XZ library to unzip BLOBs is here.
We have a DLL, written in Delphi, being called by a Java app. Initially we had issues when using PChar or ShortString but we changed these to PAnsiChar and all our issues seemed to have been solved.
However, when we started deploying the DLL to our clients, about 50% of the installations get the following error: Invalid memory access.
The very first line in the DLL is to write to our log file but that is not happening which indicated there is a problem between the Delphi and Java datatypes. Does anybody have any ideas as to what Delphi and Java data types work well together?
Delphi DLL code:
function HasCOMConnection(COMServerName: PAnsiChar): Boolean; stdcall;
begin
WriteLog('HasCOMConnection: DLL entered');
Result := HasConnection(COMServerName);
end;
exports
HasCOMConnection;
Calling from Java:
private interface IPMOProcessLabResult extends com.sun.jna.Library {
boolean HasCOMConnection(String COMServerName);
}
private boolean canConnectToCOMServer() {
try {
IPMOProcessLabResult lib = (IPMOProcessLabResult) Native.loadLibrary(config.libraryName, IPMOProcessLabResult.class);
return lib.HasCOMConnection(config.comServerName);
}
catch (Exception ex) {
new AppendLog(new Date(), this.getClass() + "\t" + ex.getClass() + "\t" + "Exception while trying to connect to COMServer: " + ex.getMessage(), "debug");
return false;
}
}
Per the Java JNA documentation, a Java String is converted to a const char* when passed to native code:
Java Strings perform the same function as the native types const char* and const wchar_t* (NUL-terminated arrays). In order to use the proper type when calling a native function, we have to introduce some sort of annotation to identify how the java Stringshould be converted. Java Strings are normally converted to char* since this is the most common usage of strings. Strings are automatically converted to a NUL-terminated array of characross the function call. Returned char* values are automatically copied into a String if the method signature returns String (strdup, for example).
So the use of PAnsiChar on the Delphi side is correct when passing a String as-is.
However, Delphi strings in Delphi 2009+ are natively encoded in UTF-16, same as Java strings. So, it would be more efficient (or at least, no risk of data loss) to use WString on the Java side:
The WString class is used to identify wide character strings. Unicode values are copied directly from the Java char array to a native wchar_t array.
And use PWideChar on the Delphi side to match, eg:
function HasCOMConnection(COMServerName: PWideChar): Boolean; stdcall;
private interface IPMOProcessLabResult extends com.sun.jna.Library {
boolean HasCOMConnection(WString COMServerName);
}
That being said, there are 2 other problems with your code.
Per the same JNA documentation, a Java boolean maps to a native int, not a bool, so your Delphi code needs to use Integer (or Int32) or better LongBool, eg:
function HasCOMConnection(COMServerName: PAnsiChar{or PWideChar}): LongBool; stdcall;
More importantly, if a native library uses the stdcall calling convention, you have to extend IPMOProcessLabResult from com.sun.jna.win32.StdCallLibrary, eg:
private interface IPMOProcessLabResult extends com.sun.jna.StdCallLibrary
Otherwise, if you extend from com.sun.jna.Library then you need to use cdecl on the native side:
function HasCOMConnection(COMServerName: PAnsiChar{or PWideChar}): LongBool; cdecl;
Created a Jar file with two classes which are dependent.
Loaded the jar from SQLite
The classes inside the jars are in Invalid state.
To call the class, Created a function as below
Classes are valid now.
Calling the class as
loadjava -user schema/Pwd jarName.jar
CREATE OR REPLACE FUNCTION checkclass1 RETURN VARCHAR2
AS
LANGUAGE JAVA NAME 'Calling.class2fn () return java.lang.String';
select checkclass1 from dual;
ORA-29531: no method class2fn in class Calling
29531. 00000 - "no method %s in class %s"
*Cause: An attempt was made to execute a non-existent method in a
Java class.
*Action: Adjust the call or create the specified method.
Should show the output of the dependent class as
Hello World
I have to use some java function in Oracle SQL Developer. But Im having some troubles with java String parameter. I know my code does nothing with this String. It will be.
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "StringTest" AS
public class StringTest
{
public static int test(String a)
{
return 1;
}
}
/
Which returns:
Java Source StringTest created
Then:
CREATE OR REPLACE FUNCTION F_STRING(input1 in char) return number
as LANGUAGE JAVA NAME 'StringTest.test(String) return int';
Function F_STRING compiled
Now when I try to execute my function:
SELECT F_STRING("some_text") FROM MyTable;
ORA-00904: "some_text": invalid identifier
00000 - "%s: invalid identifier"
When I try using single quote instead of " I get this:
ORA-29531: no method test in class StringTest
29531. 00000 - "no method %s in class %s"
*Cause: An attempt was made to execute a non-existent method in a
Java class.
*Action: Adjust the call or create the specified method.
Same thing happens when I use varchar2 instead of char.
Im sure Im missing something very simple, but can't find solution for like few hours and it already drives me crazy.
When you publishing java method in oracle you have to use full class name. (Canonical Name).
int - is ok ,
String - is not ok.
Change this
CREATE OR REPLACE FUNCTION F_STRING(input1 in char) return number
as LANGUAGE JAVA NAME 'StringTest.test(String) return int';
to this.
CREATE OR REPLACE FUNCTION F_STRING(java.lang.String in char) return number
as LANGUAGE JAVA NAME 'StringTest.test(java.lang.String) return int';
Oracle will not tolerate String use java.lang.String instead.
..Also
Not overuse " sign since this forces you to use exact case call to object, since this is not a big problem with java source it may be with other objects.
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED stringtest AS
public class StringTest
{
public static int test(String a)
{
return 1;
}
}
CREATE OR REPLACE FUNCTION F_STRING(input1 in char) return number
as LANGUAGE JAVA NAME 'StringTest.test(java.lang.String) return java.lang.Intiger';
SELECT F_STRING('some_text') FROM dual;
Situation: I have a library of JNI files, the library is comprised of several functions that are called by the main header file in that JNI library (i.e., code1.h). I have a Java file (i.e., code2.java) that I want to pass to and from JNI header file (code1.h). I created a source code for the (code1.h) called (code1.c).
My question is: Does (code1.h), (code1.c), and (code2.java) have to be the same name for the communication between the JNI and the java?
EDIT: So (code1.h), (code1.c), and (code1.java) all have to be the same name in order for the (code1.java) to pass strings to/from (code1.c)/(code1.h)? And it is not possible to have (code2.java) pass strings to/from (code1.c)/(code1.h) because they are not named the same, is this correct?
For instance,
public class code1 { /*this is code2.java, but should the name be changed to (code1.java) to match that of the JNI?*/
static {
System.loadLibrary("myjni");
}
to pass strings to code1.h/code1.c
This will be compiled for android using Linux Debian"Wheezy" and Eclipse with Android SDK and NDK
While Java requires a match between compilation unit name (SomeClass.java being the name and public class SomeClass{ being the declaration, C does not require this.
You may name the C source and header files as you see fit as long as the function names/exported symbol names match the name of the native method on the java side. For example:
//JavaClass.java
public class JavaClass{
public native String getAString(String in);
}
And header would be:
// any name
JNIEXPORT jstring JNICALL
Java_JavaClass_getAString(JNIEnv *, jobject, jstring);
with matching C files. You could name this header catsMakeTheWorldGoRound.h for all Java cares.
Here is an example of what your "JNI object" should look like.
//In my experience, it is better to put the JNI object into a separate package.
package org.example;
public class Code1
{
static
{
// On a Linux system, the actual name of the library
// is prefixed with "lib" and suffixed with ".so"
// -- e.g. "myjni-java.so"
// Windows looks for "myjni-java.dll"
//
// On a Windows system, we also need to load the prequisite
// libraries first. (Linux loaders do this automatically).
//
String osname = System.getProperty("os.name");
if (osname.indexOf("win") > -1 || osname.indexOf("Win") > -1)
{
System.loadLibrary("myjni");
}
System.loadLibrary("myjni-java");
}
// Now we declare the C functions which we will use in our Java code.
public static native void foo(int bar);
public static native int bar(String foo);
//...
}
Given that you have compiled your JNI library correctly, you can then call the C functions from other Java classes like this:
//Again, in my experience, it is better to explicitly give the package name here.
org.example.Code1 Code1= new org.example.Code1();
Code1.foo(123);
int a= Code1.bar("Hello C function from Java function!");
Does this help you with your question? (I am not an expert in JNI, so I might not be able to help further.)