Disclaimer

The views and opinions expressed in this blog are my own and do not necessarily reflect those of my employer. The views and opinions expressed by visitors to this blog are theirs and do not necessarily reflect my own

Wednesday, May 16, 2007

Call Java Methods from ApEx

For those new to ApEx it may not be immediately obvious that it is possible to execute Java Methods from ApEx. Newbies to Oracle database programming may also be surprised to know that you can actually embed Java in the database at all! The fact that this is possible make the Oracle Database Server very powerful making almost everything possible. If you cant code it in PL/SQL then try Java, If you can't use Java then perhaps you are doing something wrong (or need to go back to basics).

Why would you want to call Java from ApEx applications?
PL/SQL is great but there are occasions when you may need to perform complex processing that simply is not possible with PL/SQL, is too complex to implement or requires the use of third party APIs. One such example may be an image conversion process, which converts a GIF to a JPEG programmatically (granted I know this is possible with Oracle Intermedia).

As regular readers know I’m currently involved in a piece of work which requires various third party Java APIs which can strip out user entered data from a PDF to XML. We then process this relationally using good old pl/sql and relational views over xml. Apologies to those who thought this post might be looking at that topic, I am still writing it up and need to prepare suitable example, which can be downloaded and played with, so please bear with me.

Pre-requisites
  • Oracle Database with Java Support ( See Product Matrix )
  • A DB schema with relevant schema privileges to execute Java.
  • ApEx (pretty much any version will do)
How do you do it?
Calling Java from the Database is a fairly trivial task typically consisting of the following 3 steps.
  • Writing the Java Classes
  • Loading the classes/code into a target database schema
  • Creating a PL/SQL Wrapper round the Java Method you wish to call.
If you need to load pre-compiled java classes into your schema you can use a very useful tool called loadjava. You will need to use this utility if you have existing JAR files and APIs. Hopefully I’ll explore loadjava in a later post, for the moment I would like to concentrate on demonstrating a very simple example of calling a Java method from within Apex. I’m afraid that means it is a “hello world” example as it does not require any pre-compiled classes to be loaded into the database.

Step 1. Create Java Class
The following code creates and compiles a java class called HelloWorldExample. Notice that any method you wish to call from pl/sql must be declared as static. You can copy and paste the following code into sqlplus or sqldeveloper.

CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "HelloWorldExample"
AS
public class HelloWorldExample
{
HelloWorldExample hello;

public HelloWorldExample()
{
hello = new HelloWorldExample();
}
public static void sayHello(String forename, String surname
                      , String outString[])
{
outString[0] = "HELLO WORLD more specifically hello" + forename
          + " " + surname;
}
public static void main(String[] args)
{
//no method
}
};
/
show errors java source "HelloWorldExample";
/
You should see :
AND COMPILE Compiled.
No Errors.
Step 2: Create a pl/sql call spec/wrapper
In order to use your Java method from Apex or PL/SQL you must create a pl/sql call specification wrapper round each Java method you wish to run from PL/SQL.

Again copy and past the following code into sqlplus/sqldeveloper:
CREATE OR REPLACE PROCEDURE HELLO_WORLD(p1_forename in VARCHAR2
, p2_surname in varchar2, p3_outString out varchar2)
AUTHID CURRENT_USER AS LANGUAGE
JAVA NAME  'HelloWorldExample.sayHello(java.lang.String
, java.lang.String , java.lang.String[])';
/
show err;
/
Step 3: Execute to test 
Executing this Hello World example is as easy as declaring an anonymous block and calling the method from within within. For example:
declare
p_forename varchar2(4000) := 'KRISTIAN';
p_surname  varchar2(4000) := 'JONES';
p_msg      varchar2(4000) := null;
begin
hello_world(p_forename, p_surname, p_msg);
dbms_output.put_line(p_msg);
end;

Again if completed correctly you will see :
Anonymous block completed
HELLO WORLD more specifically hello KRISTIAN JONES
Hopefully this has served as a gentle introduction into embedding Java classes in the database. You can call this from procedure from ApEx page as a pl/sql process just substitute in your page items. If anyone has any queries please leave a comment. I'll do my best to reply.

5 comments:

Steve Muench said...

If you begin doing Java stored procedures more frequently, the free Oracle JDeveloper 10g tool makes creating, compiling, debugging, and deploying the Java stored procedures very easy. It automatically creates the package spec for you so you just click and go to deploy. Can be a great companion to SQLDeveloper and ApEx for Java stored procedure development.

Kris said...

Hi Steve,
Thanks for your comments. You make an excellent point, like you say when you are creating and editing Java Stored Procedures on a regular basis, the JDev approach is definitely the most efficient and productive way to go about things.

However, I know when I started programming in Java & C# at Uni that some of IDEs (More notably the Microsoft ones) can hide alot of detail and you don't actually learn or understand what is actually happening under the bonnet/hood hence the complete beginner perspective I tried to take with this post.

Again thanks for the comments, I'll be sure to mention JDev next time.
-- Kris

Vegesana's said...

This is great piece, I learned few things from this post, I tried the example given in this post.

MichalB said...

I want to call the Oracle BI Publisher (BIP) API from an APEX application. How do I do it. Right now I'm playing around with the API using my PC with BIP installed.

deshmukhmp said...

Question:
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "preprocessing"
AS

import java.io.*;
import java.util.*;
import java.lang.*;
public class preprocessing
{
Map m= new HashMap();
public int count=0;

//Varaibles that are used to sort each transaction

Map intmap= new HashMap();
HashMap freqitemmap= new HashMap();
//public int count=0;
long numberoftransaction=0;
List nodelink = new ArrayList();


// Procedure to have record unique value for each unique variable in file

public void generateuniqueint(String input_transaction)
{
String []str2=input_transaction.split(",");
for(String str:str2)
{
//To check whether the given String is in the map or not
//if it is in map do nothing
//else put it in the map and assign integer value to it for preprocessing purpose
if(!m.containsKey(str))
{
m.put(str,count);
count++;
}
}
}

//Procedure to convert given input dataset to numberical form.

public String preprocessing(String str)
{
String sample="";
String []temp=str.split(",");
int i=0;
for(String str1:temp)
{
str1=(m.get(str1)).toString();
sample=sample.concat(str1);
if(i<(temp.length-1))
{
sample=sample.concat(",");
i++;
}
}

//call method write to a file from here

//writetofile(sample);
String []temp1=sample.split(",");
int [] numeric=stringtoint(temp1);

recordfrequency(numeric);
return sample;

}



//Procedure to record occurance of each unique element in the transaction dataset.

public void recordfrequency(int []str2)
{
//m.clear();
for(int str:str2)
{

if(intmap.containsKey(str))
{
intmap.put(str,(intmap.get(str))+1);
}
else
{
intmap.put(str,1);

}

}
}
public static void main(String[] args)
{}

};
show errors java source "preprocessing"
/

I have to create this java stored procedure in oracle apex. But when I tried this the following error message is flagged.
ORA-29536: badly formed source: Encountered "<" at line 6, column 12.
Was expecting one of:
...
"[" ...
"." ...
"(" ...
4. import java.io.*;
5. import java.util.*;
6. import java.lang.*;
7. public class preprocessing
8. {
It's not accepting any java constructs such as hashmap, not even for loop. So let me know what to do in this case also i think It's not including library files.