Exception Handling with Java

On the one hand, the Java exception-handling architecture is very similar to that of PL/SQL. In Java-speak, you throw an exception and then catch it. In PL/SQL-speak, you raise an exception and then handle it.

On the other hand, exception handling in Java is much more robust. Java offers a foundation class called Exception. All exceptions are objects based on that class, or on classes derived from (extending) that class. You can pass exceptions as parameters and manipulate them pretty much as you would objects of any other class.

When a Java stored method executes a SQL statement and an exception is thrown, that exception is an object from a subclass of java.sql.SQLException. That subclass contains two methods that return the Oracle error code and error message: getErrorCode( ) and getMessage( ).

If a Java stored procedure called from SQL or PL/SQL throws an exception that is not caught by the JVM, the caller gets an exception thrown from a Java error message. This is how all uncaught exceptions (including non-SQL exceptions) are reported. Let's take a look at the different ways of handling errors and the resulting output.

Suppose that I create a class that relies on JDBC to drop objects in the database (this is drawn from an example in Oracle documentation):

/* File on web: dropany.java */import java.sql.*;import java.io.*;import oracle.jdbc.driver.*; public class DropAny { public static void object (String object_type, String object_name) throws SQLException { // Connect to Oracle using JDBC driver Connection conn = new OracleDriver( ).defaultConnection( ); // Build SQL statement String sql = "DROP " + object_type + " " + object_name; try { Statement stmt = conn.createStatement( ); stmt.executeUpdate(sql); stmt.close( ); } catch (SQLException e) {System.err.println(e.getMessage( ));} }}
Of course, it doesn't really make any sense to rely on JDBC to perform a drop object action, as this can be done much more easily in native PL/SQL. On the other hand, building it in Java makes the functionality available to other Java programs.

 

 

This version traps and displays any SQLException with this line:

} catch (SQLException e) {System.err.println(e.getMessage( ));}

I load the class into the database using loadjava, and then wrap this class inside a PL/SQL procedure as follows:

CREATE OR REPLACE PROCEDURE dropany ( tp IN VARCHAR2, nm IN VARCHAR2 )AS LANGUAGE JAVA NAME 'DropAny.object ( java.lang.String, java.lang.String)';

When I attempt to drop a nonexistent object, I will see one of two outcomes:

SQL> CONNECT scott/tigerConnected. SQL> SET SERVEROUTPUT ONSQL> BEGIN dropany ('TABLE', 'blip'); END;/PL/SQL procedure successfully completed. SQL> CALL DBMS_JAVA.SET_OUTPUT (1000000); Call completed. SQL> BEGIN dropany ('TABLE', 'blip'); END;/ ORA-00942: table or view does not exist

What you are seeing in these examples is a reminder that output from System.err.println will not appear on your screen until you explicitly enable it with a call toDBMS_ JAVA.SET_OUTPUT. In either case, however, no exception was raised back to the calling block because it was caught inside Java. After the second call to dropany, you can see that the error message supplied through the getMessage( ) method is taken directly from Oracle.

If I comment out the try and catch lines in the DropAny.obj method, I will get very different behavior, as shown:

SQL> BEGIN2 dropany ('TABLE', 'blip'); 3 EXCEPTION 4 WHEN OTHERS 5 THEN 6 DBMS_OUTPUT.PUT_LINE (SQLCODE); 7 DBMS_OUTPUT.PUT_LINE (SQLERRM); 8 END; java.sql.SQLException: ORA-00942: table or view does not exist at oracle.jdbc.kprb.KprbDBAccess.check_error(KprbDBAccess.java) at oracle.jdbc.kprb.KprbDBAccess.parseExecuteFetch(KprbDBAccess.java) at oracle.jdbc.driver.OracleStatement.doExecuteOther(OracleStatement.java) at oracle.jdbc.driver.OracleStatement.doExecuteWithBatch(OracleStatement.java) at oracle.jdbc.driver.OracleStatement.doExecute(OracleStatement.java) at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java) at oracle.jdbc.driver.OracleStatement.executeUpdate(OracleStatement.java) at DropAny.object(DropAny.java:14) -29532ORA-29532: Java call terminated by uncaught Java exception: java.sql.SQLException: ORA-00942: table or view does not exist

This takes a little explaining. Everything between:

java.sql.SQLException: ORA-00942: table or view does not exist

and:

-29532

represents an error stack dump generated by Java and sent to standard output, regardless of how you handle the error in PL/SQL. In other words, even if my exception section looked like this:

EXCEPTION WHEN OTHERS THEN NULL;

I would still get all that output on the screen, and then processing in the outer block (if any) would continue. The last three lines of output displayed are generated by the calls to DBMS_OUTPUT.PUT_LINE. Notice that the Oracle error is not ORA-00942, but instead is ORA-29532, a generic Java error. This is a problem. If you trap the error, how can you discover what the real error is? Looks like it's time for Write-A-Utility Man!

It appears to me that the error returned by SQLERRM is of this form:

ORA-29532: Java call ...: java.sql.SQLException: ORA-NNNNN ...

So I can scan for the presence of java.sql.SQLException and then SUBSTR from there. The O'Reilly site contains a program in the getErrorInfo.sp file that returns the error code and message for the current error, building in the smarts to compensate for the Java error message format.

The main focus in the following sections is an expansion of the JDelete class into the JFile class, which will provide significant new file-related features in PL/SQL. Following that, we'll explore how to write Java classes and PL/SQL programs around them to manipulate Oracle objects.

22.8.6 Extending File I/O Capabilities

Oracle'sUTL_FILE package is notable more for what it is missing than for what it contains. With UTL_FILE, you can read and write the contents of files sequentially. That's it. You can't delete files, change privileges, copy a file, obtain the contents of a directory, set a path, etc., etc. Java to the rescue! Java offers lots of different classes to manipulate files. You've already met the File class and seen how easy it is to add the "delete a file" capability to PL/SQL.

I will now take my lessons learned from JDelete and the rest of this chapter and create a new class called JFile, which will allow PL/SQL developers to answer the questions and take the actions listed here:

· Can I read from a file? Write to a file? Does a file exist? Is the named item a file or a directory?

· What is the number of bytes in a file? What is the parent directory of a file?

· What are the names of all the files in a directory that match a specified filter?

· How can I make a directory? Rename a file? Change the extension of a file?

I won't explain all the methods in the JFile class and its corresponding package; there is a lot of repetition, and most of the Java methods look just like the delete( ) function I built at the beginning of the chapter. I will instead focus on the unique issues addressed in different areas of the class and package. You can find the full definition of the code in the following files on the O'Reilly site:

 

JFile.java

A Java class that draws together various pieces of information about operating system files and offers it through an API accessible from PL/SQL.

 

xfile.pkg

The PL/SQL package that wraps the JFile class. Stands for "eXtra stuff for FILEs."

Oracle9i Release 2 offers an enhanced version of the UTL_FILE package that, among other things, allows you to delete a file using the UTL_FILE.FREMOVE procedure. It also supports file copying (FCOPY) and file renaming (FRENAME).