DBMS_JAVA Appendix A

advertisement
Appendix A
DBMS_JAVA
The DBMS_JAVA package is somewhat of an enigma. It is a PL/SQL package but it is not documented in the
Supplied PL/SQL Packages Reference guide. It is designed to support Java in the database, so you might expect to find
it in the Supplied Java Packages Reference guide (but you won't). It is actually documented in the Oracle8i Java
Developer's Guide. We've used it many times in this book already without really going through it, so here we will
cover the procedures I use within this package, how to use them, and what they do.
The DBMS_JAVA package has almost 60 procedures and functions, only a very small handful of which are useful to
us as developers. The bulk of this package is in support of debuggers (not for us to debug with, but for others to
write debuggers for us), various internal convenience routines, and the export/import utilities. We will skip these
functions and procedures altogether.
LONGNAME and SHORTNAME
These are utility routines to convert between a 'short' 30-character identifier (all Oracle identifiers are 30 characters
or less), and the 'long' Java name. If you look in the data dictionary, you will typically find a 'hashed' name for the
Java classes that are loaded into the database. This is because they come with really long names, which the server
cannot deal with. These two routines allow you to see what the 'real' name is, given a short name (OBJECT_NAME
column in USER_OBJECTS), and what the short name would be given a long name. Here is an example of the
usage of each when logged in as the user SYS (who happens to own lots of Java code, if you have Java installed in
the database):
1050
5254AppAD.pdf 1
2/28/2005 6:49:34 PM
DBMS_JAVA
sys@TKYTE816> column long_nm format a30 word_wrapped
sys@TKYTE816> column short_nm format a30
sys@TKYTE816> select dbms_java.longname(object_name) long_nm,
2
dbms_java.shortname(dbms_java.longname(object_name)) short_nm
3
from user_objects where object_type = 'JAVA CLASS'
4
and rownum < 11
5 /
LONG_NM
SHORT_NM
------------------------------ -----------------------------com/visigenic/vbroker/ir/Const /1001a851_ConstantDefImpl
antDefImpl
oracle/sqlj/runtime/OraCustomD /10076b23_OraCustomDatumClosur
atumClosure
com/visigenic/vbroker/intercep /10322588_HandlerRegistryHelpe
tor/HandlerRegistryHelper
...
10 rows selected.
As you can see, using LONGNAME on the OBJECT NAME turns it into the original class name for the Java class. If
we take this long name and pass it through SHORTNAME, we get back the hashed-shortened name Oracle uses
internally.
Setting Compiler Options
You may specify most compiler options for the Java compiler in the database, in one of two places; the command
line when using loadjava, or in the JAVA$OPTIONS database table. A setting on the command line will always
override the JAVA$OPTIONS table. This only applies if you use the Oracle Java compiler in the database, of
course. If you use a standalone Java compiler outside of the database (JDeveloper perhaps), you will set compiler
options in that environment.
There are three compiler options we may set, and they all relate to the SQLJ compiler (a pre-compiler for Java,
converts embedded SQL statements into JDBC calls) built-in to the database. They are:
Option
Meaning
Values
ONLINE
Whether type checking is done at compile-time (online),
or run-time.
True/False
DEBUG
Whether the Java code is compiled with debugging
enabled. Equivalent to javac -g in a command line
environment.
True/False
ENCODING
Identifies the source file encoding for the compiler.
Latin1 is the default
The values in bold are the default settings.
1051
5254AppAD.pdf 2
2/28/2005 6:49:34 PM
Appendix A
We'll demonstrate the use of DBMS_JAVA to set compiler options using the online SQLJ pre-compiler option.
Normally, this option defaults to True, and will cause the SQLJ pre-compiler to attempt to perform semantic
checking on our SQLJ code. What this means is that the SQLJ pre-compiler would normally verify each and every
referenced database object exists, that the host variable bind types match, and so on. If you would like this checking
to be performed at run-time (perhaps the tables your SQLJ code will access are not yet created, but you would like
to install your code cleanly), we can use the DBMS_JAVA.SET_COMPILER_OPTIONS routine to disable this type
checking.
As an example, we'll use this snippet of code. It attempts to INSERT into a table that does not exist in the database:
tkyte@TKYTE816> create or replace and compile
2 java source named "bad_code"
3 as
4 import java.sql.SQLException;
5
6 public class bad_code extends Object
7 {
8 public static void wont_work() throws SQLException
9 {
10
#sql {
11
insert into non_existent_table values ( 1 )
12
};
13 }
14 }
15 /
Java created.
tkyte@TKYTE816> show errors java source "bad_code"
Errors for JAVA SOURCE bad_code:
LINE/COL ERROR
-------- ----------------------------------------------------------------0/0
bad_code:7: Warning: Database issued an error: PLS-00201:
identifier 'NON_EXISTENT_TABLE' must be declared
0/0
0/0
0/0
0/0
0/0
0/0
insert into non_existent_table values ( 1 )
^^^^^^^^^^^^^^^^^^
;
#sql {
^
Info: 1 warnings
Now, we'll set the compiler option ONLINE to FALSE. In order to do this, we have to disconnect and
connect again. There is an issue whereby the Java run-time will look for the existence of the
JAVA$OPTIONS table once it starts up. If this table does not exist, it never attempts to read it again in that
session. The DBMS_JAVA.SET_COMPILER_OPTION routine will create this table for us, but only if it is
invoked prior to the Java run-time being started. So, we need a 'clean' session for this to work.
In the following example, we establish a new session, and then see that the JAVA$OPTIONS table does not exist.
We'll set the compiler option, and see that the table has been created for us. Lastly, we'll create the same Java
routine as above, and see that it compiles without warnings this time, due to the compiler option setting:
1052
5254AppAD.pdf 3
2/28/2005 6:49:35 PM
DBMS_JAVA
tkyte@TKYTE816> disconnect
Disconnected from Oracle8i Enterprise Edition Release 8.1.6.0.0 - Production
With the Partitioning option
JServer Release 8.1.6.0.0 – Production
tkyte@TKYTE816> connect tkyte/tkyte
Connected.
tkyte@TKYTE816> column value format a10
tkyte@TKYTE816> column what format a10
tkyte@TKYTE816> select * from java$options;
select * from java$options
*
ERROR at line 1:
ORA-00942: table or view does not exist
tkyte@TKYTE816> begin
2
dbms_java.set_compiler_option
3
( what
=> 'bad_code',
4
optionName => 'online',
5
value
=> 'false' );
6 end;
7 /
PL/SQL procedure successfully completed.
tkyte@TKYTE816> select * from java$options;
WHAT
OPT
VALUE
---------- -------------------- ---------bad_code
online
false
tkyte@TKYTE816> create or replace and compile
2 java source named "bad_code"
3 as
4 import java.sql.SQLException;
5
6 public class bad_code extends Object
7 {
8 public static void wont_work() throws SQLException
9 {
10
#sql {
11
insert into non_existent_table values ( 1 )
12
};
13 }
14 }
15 /
Java created.
tkyte@TKYTE816> show errors java source "bad_code"
No errors.
1053
5254AppAD.pdf 4
2/28/2005 6:49:35 PM
Appendix A
The SET_COMPILER_OPTION takes three inputs in this case:
❑
WHAT – A pattern to be matched against. Normally, Java programs would use packages and
hence, the above name would be a.b.c.bad_code, not just bad_code. If you want to set an
option for a package a.b.c, you may. Then, anything that matched a.b.c would use this
option, unless there was a more specific pattern, which matches this package. Given a WHAT of
a.b.c, and a.b.c.bad_code, then a.b.c.bad_code would be used, since it matches more
of the name.
❑
OPTIONNAME – One of the three values ONLINE, DEBUG, or ENCODING.
❑
VALUE – The value for that option.
There are two routines related to SET_COMPILER_OPTION. They are:
❑
GET_COMPILER_OPTION – This returns the value of a given compiler option, even if the value
is defaulted.
❑
RESET_COMPILER_OPTION – This removes any row from the JAVA$OPTIONS table that
matches the WHAT pattern, and the OPTIONNAME.
Here are examples of both in action. We'll begin by using GET_COMPILER_OPTION to see the value of the online
option:
tkyte@TKYTE816> set serveroutput on
tkyte@TKYTE816> begin
2
dbms_output.put_line
3
( dbms_java.get_compiler_option( what
=> 'bad_code',
4
optionName => 'online' ) );
5 end;
6 /
false
PL/SQL procedure successfully completed.
and now we'll reset it using RESET_COMPILER_OPTION:
tkyte@TKYTE816> begin
2
dbms_java.reset_compiler_option( what
=> 'bad_code',
3
optionName => 'online' );
4 end;
5 /
PL/SQL procedure successfully completed.
Now we'll see that GET_COMPILER_OPTION will always return us a value for the compiler option, even though
the JAVA$OPTIONS table is now empty (the RESET deleted the row):
tkyte@TKYTE816> begin
2
dbms_output.put_line
3
( dbms_java.get_compiler_option( what
=> 'bad_code',
4
optionName => 'online' ) );
5 end;
6 /
1054
5254AppAD.pdf 5
2/28/2005 6:49:35 PM
DBMS_JAVA
true
PL/SQL procedure successfully completed.
tkyte@TKYTE816> select * from java$options;
no rows selected
SET_OUTPUT
This procedure is a lot like the SQL*PLUS command SET SERVEROUTPUT ON. Just as you need to use it to
enable DBMS_OUTPUT, we need to use DBMS_JAVA.SET_OUTPUT to enable the results of
System.out.println and System.err.print calls to come to the screen in SQL*PLUS. If you fail to call:
SQL> set serveroutput on size 1000000
SQL> exec dbms_java.set_output( 1000000 )
before running a Java stored procedure in SQL*PLUS, you must be aware that any of its
System.out.println messages will be written to a trace file in the directory specified by the
USER_DUMP_DEST init.ora parameter on the server. This procedure is truly useful when debugging Java
stored procedures, as you can put calls to System.out.println in the code, much as you would put
DBMS_OUTPUT.PUT_LINE calls in your PL/SQL. Later, you can disable this in your Java code by redirecting
System.out to the 'bit bucket'.
So, if you ever wondered where your System.out calls where going in a Java stored procedure, now you know.
They were going to a trace file. Now you can cause that output to come to your screen in SQL*PLUS.
loadjava and dropjava
These functions provide PL/SQL APIs to perform the job of the command line utilities loadjava and
dropjava. As you might expect with these internal routines, you do not need to specify a -u
username/password, or specify the type of JDBC driver to use – you are already connected! These routines will
load the Java objects into the currently logged in schema. The supplied routines are:
PROCEDURE loadjava(options varchar2)
PROCEDURE loadjava(options varchar2, resolver varchar2)
PROCEDURE dropjava(options varchar2)
We could use this to load the activation8i.zip file, which we also use in the UTL_SMTP section, and more
information on JavaMail API can be found at http://java.sun.com/products/javamail/index.html. For
example:
sys@TKYTE816> exec dbms_java.loadjava( '-r -v -f -noverify -synonym -g p
ublic c:\temp\activation8i.zip' )
initialization complete
loading : com/sun/activation/registries/LineTokenizer
creating : com/sun/activation/registries/LineTokenizer
1055
5254AppAD.pdf 6
2/28/2005 6:49:35 PM
Appendix A
loading
creating
loading
creating
loading
creating
...
:
:
:
:
:
:
com/sun/activation/registries/MailcapEntry
com/sun/activation/registries/MailcapEntry
com/sun/activation/registries/MailcapFile
com/sun/activation/registries/MailcapFile
com/sun/activation/registries/MailcapParseException
com/sun/activation/registries/MailcapParseException
Permission Procedures
These are strange ones indeed. Do a DESCRIBE on DBMS_JAVA in the database, and tell me if you see
GRANT_PERMISSION in that package. You won't, although you know it must exist since you've seen me use it
quite a few times. It does exist, as do a couple of other permission-related functions. We'll describe the
GRANT_PERMISSION/REVOKE_PERMISSION here, and its usage. For complete details on using the permissions
routines, and all of the options, refer to the Oracle Java Developers Guide. Chapter 5 in this manual, Security for Oracle
8i Java Applications, covers these functions.
In Oracle 8.1.5, the granularity of privileges in Java was very coarse. You either had JAVAUSERPRIV or
JAVASYSPRIV, pretty much. This would be like having just RESOURCE and DBA roles in the database – in both
cases these roles may offer too much functionality to the end users. With Oracle 8.1.6, the Java in the database
supports the Java 2 security classes. Now we have very granular privileges we can grant and revoke, just like the
database has for its privilege set. For a general discussion and overview of these permission classes, I'll refer you to
this web page http://java.sun.com/j2se/1.3/docs/api/java/security/Permission.html.
So, the two main APIs we'll use here are GRANT_PERMISSION and REVOKE_PERMISSION. The question is,
how do I find out what permissions I need? The easiest way is to install the Java, run it, and see what it tells you it
needs. For example, I will refer you to the UTL_SMTP section. In there, I create the stored procedure SEND to send
mail. I also show you the two grants we need to perform with GRANT_PERMISSION in order to get that to work.
The way in which I discover exactly what those grants was to run SEND and see how it fails. For example:
tkyte@TKYTE816> set serveroutput on size 1000000
tkyte@TKYTE816> exec dbms_java.set_output( 1000000 )
PL/SQL procedure successfully completed.
tkyte@TKYTE816> declare
2
ret_code number;
3 begin
4
ret_code := send(
5
p_from => 'me@here.com',
6
p_to => 'me@here.com',
7
p_cc => NULL,
8
p_bcc => NULL,
9
p_subject => 'Use the attached Zip file',
10
p_body => 'to send email with attachments....',
11
p_smtp_host => 'aria.us.oracle.com',
12
p_attachment_data => null,
13
p_attachment_type => null,
14
p_attachment_file_name => null );
15
if ret_code = 1 then
1056
5254AppAD.pdf 7
2/28/2005 6:49:35 PM
DBMS_JAVA
16
dbms_output.put_line ('Successful sent message...');
17
else
18
dbms_output.put_line ('Failed to send message...');
19
end if;
20 end;
21 /
java.security.AccessControlException: the Permission (java.util.Property
Permission * read,write) has
not been granted by dbms_java.grant_permission to
SchemaProtectionDomain(TKYTE|PolicyTableProxy(TKYTE))
Now, that is about as clear as you can get. It is telling me that TKYTE needs the permission type
java.util.PropertyPermission with * and read and write. This is how I knew I needed to execute:
sys@TKYTE816> begin
2
dbms_java.grant_permission(
3
grantee => 'TKYTE',
4
permission_type => 'java.util.PropertyPermission',
5
permission_name => '*',
6
permission_action => 'read,write'
7
);
After I did this, I discovered the error:
java.security.AccessControlException: the Permission (java.net.SocketPer
mission aria.us.oracle.com resolve) has not been granted by
dbms_java.grant_permission to
SchemaProtectionDomain(TKYTE|PolicyTableProxy(TKYTE))
and after granting that, it told me I needed CONNECT in addition to RESOLVE. This is how I knew to add:
8
9
10
11
12
13
14
15
dbms_java.grant_permission(
grantee => 'TKYTE',
permission_type => 'java.net.SocketPermission',
permission_name => '*',
permission_action => 'connect,resolve'
);
end;
/
to the privileges that this schema had. Note that I used * in the permission_name so I could actually resolve
and connect to any host, not just my SMTP server.
Now, the opposite of GRANT_PERMISSION is REVOKE_PERMISSION. It operates exactly as you might think. If
you pass it the same exact parameters you pass to GRANT_PERMISSION, it will revoke that privilege from the
schema.
1057
5254AppAD.pdf 8
2/28/2005 6:49:35 PM
Appendix A
Summary
In this section, we covered using the DBMS_JAVA package to perform various operations for us. We started out by
looking at how Oracle, which has a 30-character name limit, handles the very long names used in Java. It hashes a
unique, 30-character name for each of the long Java names. The DBMS_JAVA package gives us a function to
convert either a short name back into its corresponding long name, or to convert a long name into its short name
representation.
Next we investigated using DBMS_JAVA to set, retrieve, and reset various Java compiler options. We saw how this
feature uses the JAVA$OPTIONS table to permanently store default compiler options for us, and how we can use it
to reset these values back to their defaults. Then we looked briefly at the SET_OUTPUT routine. This redirects the
output generated by System.out.println Java calls to a SQL*PLUS or SVRMGRL session, much in the
same way SET SERVEROUTPUT ON does for the PL/SQL routine DBMS_OUTPUT. We also saw how the
DBMS_JAVA package provides an alternative method of loading Java source code, class files and jars into the
database, via a stored procedure call in Oracle8i release 2 (version 8.1.6) and up. Lastly, we looked at the
permission procedures provided by this package in Oracle8i release 2 and up. This interface allows us to grant very
granular privileges to our Java routines, allowing us to strictly control what they can, and cannot do.
All in all, if you are using Java inside the Oracle database, you will find these routines invaluable in your day-to-day
programming.
1058
5254AppAD.pdf 9
2/28/2005 6:49:35 PM
Download