Ads 468x60px

Monday, July 25, 2011

Extending Java Using Native Methods in JAVA

Extending Java Using Native Methods

One of the best aspects of Java is its platform independence. Any applet that you write basically performs in exactly the same manner, regardless of the platform or operating system of the host computer. Thanks to Java's broad support for everything from GUI windowing in the java.awt classes to networking support in the class, most tasks can be accomplished directly within Java.

Even though Java is platform independent, there are some platform-specific bugs. For example, in the Windows 95 AWT implementation, windows shown modally do not actually behave modally. The status of bugs in Sun's Java interpreter is available at

Because Java is platform independent, however, it doesn't support all of the features of its host computer or operating system. The Win32 API supported by Microsoft Windows NT and Windows 95 includes, among many other useful features, a set of functions for establishing network connections with the modem, using Remote Access Services (RAS). Programmatically establishing a remote connection to a network can be very useful to support, for example, automatic registration of a commercial Java-based application.
Another reason to use native methods is the multitude of libraries that provide C interfaces to various systems. Many APIs are currently supplied as a set of statically linked library files with associated C header files and possibly some dynamically linked libraries. Unfortunately, very few APIs are currently supplied with Java wrapper classes.
There are basically two means of accessing non-Java libraries; the first, the use of separate processes, has been discussed earlier in this chapter. The disadvantage of using a separate process is its loose integration with the Java environment. Parameter passing is very limited, and communication between Java and the separate process and runtime may be impossible, or simply too tedious. The second means of accessing non-Java libraries is through the use of native methods.
Native methods are methods defined within Java classes using the native keyword. Within the java class, they have no implementation specified-only the name of the method, access specification, parameters, and return value.

Basic Mechanics of Creating Native Methods

When creating a native method, you must first define it in a java class using the native keyword. For example, to define a public native method returning an integer called fastStringScan(String str, String strToFind) in a class named StringUtils, you would use the following code:
class StringUtils {
    public native int fastStringScan(String str, String strToFind);
Literally any Java method, with the exception of object constructors, can be implemented as a native method. If you need to call some function implemented as a native method during the creation of your object, you can create a private native method that performs the initialization and call the method when the constructor executes.

Obviously, the implementation of the native method must reside somewhere. It is typically part of a dynamically-linked library; on the Microsoft Windows 95 or Windows NT platform it is in a DLL.
Java provides tools to generate wrappers for native code implementations in C. The wrappers generated by Java provide a fairly easy-to-use interface between native method implementations and the Java runtime environment. The use of wrappers is not optional; the Java interpreter expects to find functions with specific names determined from the native method definition. Implement the following steps to create native code wrappers:
  1. Compile the java class that contains the native method declaration.
  2. This is done by running javac from the command prompt.
  3. Create a header file that declares the structure representing the java class.
  4. The Java JDK provides a utility, javah, that does this for you. Typing javah MyClass from the command line generates a MyClass.h file containing the structure of the class (as used by native methods) and native method function prototypes.
  5. Create a stub file that contains function wrappers that call the native functions you implement.
  6. Using javah -stubs MyClass generates a file called MyClass.c that contains function stubs. A section later in this chapter, "The Stubs File," provides more information about the stubs file.
  7. Develop the implementation of the native methods.
The header file created in step 2 contains the structure definitions and function prototypes that you need to implement your native methods. You must implement each function listed in the header file.

Be very careful if you add another member to your class! Make sure to recompile the java class with the native method declarations, and make sure that you rerun the javah utility and recompile your DLL. Failing to recompile or rerun can lead to some very frustrating-but not straightforward-problems! You may set a class member, but continue to see it as null because the offsets calculated by the compiler no longer match up with the java class declaration.
It's definitely worthwhile to modify your makefile to include a dependency step that updates the native method header file.

The Stubs File

The following are the contents of the StringUtils.c stub file generated from the previously mentioned StringUtils class.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <StubPreamble.h>

/* Stubs for class StringUtils */
/* SYMBOL: "StringUtils/fastStringScan(Ljava/lang/String;
ÂLjava/lang/String;)I", Java_StringUtils_fastStringScan_stub */
__declspec(dllexport) stack_item *Java_StringUtils
ÂfastStringScan_stub(stack_item *_P_,struct execenv *_EE_) {
    extern long StringUtils_fastStringScan(void *,void *,void *);
    P_[0].i = StringUtils_fastStringScan(_P_[0].p,((_P_[1].p)),((_P_[2].p)));
    return _P_ + 1;
The Stubs file contains function stubs that Java expects when it attempts to call a function with a dynamic link library that implements a native method. The included StubPreamble.h file contains all of the type and structure definitions required by Java stub functions. Notice that all of the stub functions are exported using the _declspec(dllexport) directive. They are the entry points to the DLL that contains the native methods; the functions that you write do not need to be exported.
The stub functions basically repackage the arguments from a Java interpreter function call into single parameters with specific types. The internal prototype, in this case extern long StringUtils_fastStringScan(void *,void *,void *), uses void pointers for all of the arguments. However, the function that you implement has specific parameter types. The function declarations for the functions that you implement are contained within the StringUtils.h header file. The fastStringScan() function is defined within that file as extern long StringUtils_fastStringScan(struct HStringUtils *,struct Hjava_lang_String *,struct Hjava_lang_String *).

The Header File

The following are the contents of the StringUtils.h header file generated by javah from the previously mentioned StringUtils class.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <native.h>
/* Header for class StringUtils */

#ifndef _Included_StringUtils
#define _Included_StringUtils

typedef struct ClassStringUtils {
    char PAD;    /* ANSI C requires structures to have a least one member */
} ClassStringUtils;

#ifdef __cplusplus
extern "C" {
struct Hjava_lang_String;
extern long StringUtils_fastStringScan(struct HStringUtils *,
Âstruct Hjava_lang_String *,struct Hjava_lang_String *);
#ifdef __cplusplus
The header file contains all of the function prototypes that you need to implement, as well as all of the includes that you need to call useful Java functions. Peeking through the Java API files included automatically in the header file is a worthwhile exercise. Several include files are nested within the native.h header. Without additional documentation, it's very difficult to determine the purpose of many of the structures and functions in some of the included files; but the native.h header file can occasionally shed light on problems that you may encounter during compilation.
If you are implementing the native methods in C++, you must be sure to wrap the inclusion of the StringUtils.h header file in an extern "C" block, as the following example demonstrates:
 * StringUtilsImpl.cpp
 * Contains implementation of native methods.

extern "C" {
#include <StringUtils.h>
The StringUtils class is represented as a C structure in the header file. Accessing object instance variables involves using members of the ClassStringUtils structure; further descriptions are listed in the following sections.

Calling Java from Native Methods

It is frequently necessary to call Java methods from within native methods. In order to do so, it is essential to understand method signatures, and to know how to dispatch Java method calls. The following sections describe method signatures and method call dispatching in detail.
It is also frequently important to be able to create Java objects from within native methods. This is particularly important, for example, when returning a Java object, such as a String, from a native method.

Identifying Methods: Method Signatures

Within a class, each method is distinguished from other methods by the method's name and signature. The name is simply the name of the method. The signature is a string that describes the method parameters and the method return value. This allows Java to perform function overloading by enabling the interpreter to dynamically look up and dispatch functions that have the same name but have different arguments or return types.
Method signatures consist of a set of method parameter type descriptions enclosed within parentheses and followed by a return type description. Primitive types are designated differently from object types; object types are prefixed with an "L", include the fully distinguished name of the object class delimited by "/" rather than ".", and are terminated with a ";".
Given a Java method repeatSubString(), defined as String repeatSubString(int begOffset, int endOffset, int repeatCount, String string), the method signature would be "(IIILjava/lang/String;)Ljava/lang/String;". If an array is passed, the type of the array element should be prefixed by a "[" in the method signature. The method signature for int findString(String stringToFind, String[] strings) would be "(Ljava/lang/String;[Ljava/lang/String;)I".
The signature prefixes or characters are defined within the signature.h header file. The most frequently used signature characters follow:
[ - Array
B - Byte
C - Char
L - Beginning of Class name
; - End of Class name
F - Float
D - Double
I - Integer
J - Long
S - Short
V - Void
Z - Boolean

Calling Java Object Methods from Native Methods

You will frequently want to make a Java object perform some action from a native method. To do so, you call a method on the object. To call a method on an object from a native method, you use the execute_java_dynamic_method() function.
The full definition of the function from interpreter.h follows:
long execute_java_dynamic_method(ExecEnv *,
                                 HObject *obj,
                                 char *method_name,
                                 char *signature,
The first argument is the execution environment, or ExecEnv. You should generally use the EE() function, which returns the current execution environment. The execution environment has little or no documentation provided with the JDK 1.0 release; you can glean some of its uses by examining the execenv structure in interpreter.h and associated macros. The second argument is an object instance that provides the method you want to call. The third argument is the method name, sans signature. It is the raw name of the method without access specifiers, parameters, return types, etc. For a method defined as public int foo(String str), the method name would simply be foo. The fourth argument is the signature of the method, as described in the previous section.
The remaining arguments are the object or primitive datatype parameters required by the method. The arguments must correspond to the types defined in the method signature.
Notice that the execute_java_dynamic_method() returns a long. Your code should cast the long appropriately, given the return type defined in the method signature.
The execute_java_dynamic_method() function enables you to call methods on an object instance, but it doesn't enable you to call static methods defined for a class. Calling static methods requires the use of the execute_java_static_method() function, defined as follows in interpreter.h:
long execute_java_static_method(ExecEnv *,
                                ClassClass *cb,
                                char *method_name,
                                char *signature,
The arguments of execute_java_static_method() are almost identical to the arguments for execute_java_dynamic_method(). The only difference is the second argument, which is a pointer to a Class object rather than a Java object instance. The Java interpreter creates a Class object for every loaded class. The ClassClass structure is defined in the oobj.h header file.
You can use the FindClass() function to get a Class object with a class name; it's defined in interpreter.h as:
ClassClass *FindClass(struct execenv *ee, char *name, bool_t resolve);
To find the java.lang.System class object, you use the following function call:
ClassClass *System = FindClass(EE(), "java/lang/System", FALSE)

Accessing Java Object Instance Variables from Native Methods

One of the most common reasons to access object instance variables from native methods is to set instance variables for the object that contains the native method that you implement. Using the following definition for a NonJavaFile class:
public class NonJavaFile {
    public native void getFileAttributes(String strFile);
    public String strAttr1;
    public int iAttr2;
    public boolean bAttr3;
The native method implementation for getFileAttributes() would no doubt require the ability to set the various Attribute variables of the NonJavaFile instance. The native method shell, generated using javah as described previously, includes a parameter that is not visible in the Java getFileAttributes() method declaration. The additional parameter is the pointer to the handle of the object instance that was used to call the native method. Use the unhand() function to acquire the C structure that contains the Java object instance variables.
Using the NonJavaFile example, the implementation of getFileAttributes() might look like:
void NonJavaFile_getFileAttributes(struct HNonJavaFile *me,
                                   struct Hjava_lang_String *strFile)
    ClassNonJavaFile *NonJavaFile = (ClassNonJavaFile*)unhand(me);

    // Use the strFile argument to perform some action(s)

    // Modify some of the Java object instance variables (or, in other
    // words, members of the ClassNonJavaFile structure)
    char achAttr[] = "Some File Attribute";
    NonJavaFile->strAttr1 = makeJavaString(achAttr, sizeof(achAttr));
    NonJavaFile->iAttr2 = 100;
    NonJavaFile->bAttr3 = TRUE;

Creating Java Objects in Native Methods

The Java API function execute_java_constructor() is the key to creating Java objects from native methods. Its arguments include the name of the class to create, the desired constructor signature, and the arguments (which must, of course, correspond to the constructor signature) to the constructor. If the constructor executes successfully, it returns a pointer to a new Java object. If failure occurs, the function returns NULL, and an exception is raised.
The full prototype for the execute_java_constructor() function, as defined in interpreter.h, follows:
execute_java_constructor(ExecEnv *,
                         char *classname,
                         ClassClass *cb,
                         char *signature, ...);
If you already have a Class object for the class instance that you want to create, you can pass it in as the cb argument and pass NULL for the classname. This is somewhat faster than the alternative, which is to pass the classname and omit the Class object (pass NULL for cb), because the Java interpreter must find the Class object based on the class name.
The Java String class is a special case; the process of creating Strings is simplified by the makeJavaString() function prototyped in the javaString.h header file. The full function prototype follows:
Hjava_lang_String *makeJavaString(char *, int);
The function returns a new String object given a C character pointer and the length of the string.

Writing Well-Behaved Native Code

All of the classes provided with the Java Development Kit fully use Java's standard security, error handling, and synchronization mechanisms. Native methods aren't forced to conform to any of the aforementioned standards. Deliberate effort is required on the behalf of native method implementer to use them.
The following sections describe the standard mechanisms, as well as means of conforming to their requirements.

Error Handling in Native Methods

Java's error handling mechanism is centered around the use of exceptions. Literally any method called on any Java object may throw one or more types of exceptions. Exceptions are used to indicate that an anomalous situation occurred during the execution of method code. The members of the Exception object describe the type of exception that occurred.
Java enforces the explicit capturing or throwing of exceptions. If your Java code calls a method that indicates in its definition that it throws one or more exceptions, your calling code must either catch the exceptions, or explicitly indicate that it throws the exceptions. Use of exceptions within Java code is further described in Chapter 10, "The Order Entry System: Exception Handling and Browser Interaction."
Native methods should, when appropriate, throw Java exceptions. They should also declare the Java exceptions that can be thrown by Java class methods executed by the Java code, giving the Java compiler information that it needs to enforce Java's rules for exception capturing.
Handling Exceptions Thrown by Java Code
Within Java code, the handling of exceptions is automatic. Frames with exception handlers can be established to catch exceptions. Native methods must use the following Java API function to detect exceptions thrown by Java methods:
The exceptionOccurred() function returns true if a Java exception has been raised. To handle the exception, you can retrieve additional information about it using the exc member of the exception union within the execenv structure. The exc member contains a pointer to the Java exception object.
Typical native code that calls a Java method, then checks for and handles Java exceptions, might look like the following:
// Call some Java method
long lResult = execute_java_dynamic_method(EE(), theObj, "someMethod",
if (exceptionOccurred(EE())
    // Check if the exception that occurred is a dao.DaoException
    // in this example (NOTE: the dao.DaoException is mentioned in
    // the DAOLayer example)
    JHandle *exception = EE()->exc;
    ClassClass *DaoExceptionClass = FindClass(EE(), "dao/DaoException", TRUE);
    if (is_instance_of(exception, DaoExceptionClass, EE()))
        // The exception is a dao.DaoException, so I can include
        // code to handle the exception

        // After I have handled the exception, I clear it so that
        // it isn't propagated back to the Java interpreter
This is the rough functional equivalent of the following Java code:
try {
} catch (dao.DaoException e) {
The native code is much more involved than the Java code. That's one of the disadvantages of using native methods. Java code doesn't have to worry about explicitly testing the type of the exception that is generated-the Java interpreter matches the exception with the corresponding catch clause, if an appropriate catch clause exists. After the code in the catch clause executes, the Java interpreter handles clearing the exception and resetting the Java execution environment.
The native code clears the Java exception explicitly using exceptionClear(). The exceptionClear() function is implemented as a macro in interpreter.h; its only parameter is a pointer to an execenv structure. The exceptionClear() macro is used to clear the current exception. It should be used if you catch and handle an exception in your native method code. Use it with caution; you will want to propagate (throw) some exceptions back to the code that called your native method.
If an exception occurs, the type of the exception is tested using the is_instance_of() function, which returns true if an object is an instance of a specific class. The definition of is_instance_of, from interpreter.h, follows:
bool_t is_instance_of(JHandle * h, ClassClass *dcb, ExecEnv *ee);
The first argument is the Java object you want to test, the second is the class object that you want to check the object against, and the third is a pointer to an execenv structure. The is_instance_of function returns true if the object is an instance of the class or a subclass of the class.
Throwing Exceptions
Throwing exceptions from native methods is very straightforward. The only Java API function that is required is SignalError(). The prototype for SignalError() follows:
void SignalError(struct execenv *, char *, char *);
The first argument is a pointer to an execenv structure. You can use the EE() function to pass the active execenv structure to SignalError(). The second argument is a null-terminated C string indicating the fully distinguished name of the exception that you're throwing. As usual, the periods separating the packages in which the class is defined should be replaced with the forward slash ( / ). The third argument is a null-terminated string that describes details of the exception; you can pass null if you don't want to specify additional information.
You can learn more about Java exceptions in Chapter 10. The DAOLayer native methods example, described later in this chapter, also demonstrates throwing exceptions from native methods.

Security in Native Methods

As previously indicated, Java native methods aren't subject to the same security restrictions as pure Java methods. That's not to say that they are completely exempt; for example, if you attempted to open a file using the classes from a native method, the open call would fail with a SecurityException if the Java application doesn't have sufficient security privileges.
However, a native method could circumvent that security by using non-Java input-output mechanisms. If the native method uses standard C library functions for file input and output, it would be allowed to do so irrespective of the security restrictions of the current Java execution environment.
Consequently, to write truly well-behaved native methods, it is necessary to explicitly check the active security restrictions. As a matter of fact, this is precisely the behavior of implementations in the Java standard library. The following excerpt, from in the package, illustrates a security check:
 * Deletes the specified file. Returns true
 * if the file could be deleted.
public boolean delete() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
    return delete0();
Obviously, the security check is performed here within a Java wrapper method that calls a native method, delete0(), that actually performs the file deletion. If you need to perform a simple, single-step security check, using the wrapper method may suffice.
If you have a native method that performs several different types of actions that might each be subject to different security restrictions, it may be more convenient for you to include the security checks within the body of the native method. The following code illustrates one means of performing those types of security checks:
ClassClass *System = FindClass(EE(), "java/lang/System", TRUE);
Hobject *phSecMgr = (Hobject*)execute_java_static_method(EE(), System,
    "getSecurityManager", "()Ljava/lang/SecurityManager;");

if (NULL != phSecMgr)
    // Perform security check - for this example, check the ability
    // to delete a file in the local file system
    execute_java_dynamic_method(EE(), phSecMgr,
        "checkDelete", "(Ljava/lang/String;)V", phFile);
    if (exceptionOccurred())
         // Perform some action…
The default SecurityManager that comes with the Java development kit throws a SecurityException on every check that is performed.

Using Java's Synchronization/Wait-Notification Mechanisms

The architecture of Java was defined with the goal of directly supporting multithreading. Consequently, thread creation, notification, and synchronization mechanisms are provided within Java. Because every class is a sub-class of java.lang.Object, every class supports the wait() and notify() synchronization functions. Entire methods can be protected automatically by Java if they are defined with the synchronized keyword. The java.lang.Thread classes contain a synchronized static nextThreadNum() method; the synchronized keyword assures that the function may be called by only one Java object at a time.
Native Method Wait/Notify
To wait on a Java object you must call the object's wait() method. Doing so is straightforward, as the following code snippet illustrates:
execute_java_dynamic_method(EE(), theObject, "wait", "()V");
To notify a Java object you must call the object's notify() or notifyAll() method. The following code illustrates a call to the notify() method:
execute_java_dynamic_method(EE(), theObject, "notify", "()V");
Using wait() and notify() with Java objects is very useful when, for example, your native method consumes objects from a queue filled by a separate Java thread.
You should note that waits may be interrupted, in which case an InterruptedException is thrown by the wait method. Consequently you may want to check for a Java exception after executing the wait, as shown in the following snippet:
// Wait for the object to be notify()ed
execute_java_dynamic_method(EE(), theObject, "wait", "()V");

// Check for the occurrence of an InterruptedException
if (exceptionOccurred(EE()))
    // Handle the exception - check for Interrupted, or other...
If you don't check for the InterruptedException, you may erroneously execute code that assumes that a waited object was notified. This isn't an issue within pure Java code because exceptions are enforced by the Java interpreter.

An Interface to Microsoft Data Access Objects (DAO)

One of the most useful and easy-to-use commercial APIs is the Microsoft DAO object model for Windows 95 and Windows NT. The DAO objects provide a simple, powerful abstraction that wraps ODBC-compliant database systems. DAO objects are directly exposed as a set of COM and OLE automation classes; the Microsoft Visual C++ development environment includes the DAO C++ API, which provides a simple mechanism to use the DAO OLE classes. The DAO classes are used to access databases and tables in the previously mentioned DAOCmd project.

Microsoft recently released an open beta of their Visual J++ Java development environment, available at The Visual J++ environment, which runs on Windows 95 and Windows NT, incorporates support for the COM object model and allows the developer to instantiate and use OLE objects directly within Java, as well as create COM/OLE objects using Java. Because of the built-in COM support, Visual J++ applications can call DAO objects directly.

Microsoft Visual C++ also provides some DAO wrapper classes within the Microsoft Foundation Classes. The DAO wrappers handle some of the details of the creation and destruction of DAO objects for you. Because of their ease of use, they are a compelling choice as a set of classes to integrate with Java. The DAOLayer project, included with source code on the CD-ROM provided with this book, demonstrates fairly simple integration of Java classes with Microsoft MFC DAO C++ classes.
DAOLayer is designed to illustrate the integration of Java with Microsoft MFC DAO C++ classes using native methods. The DAOLayer project consists of several C/C++ header files and source files, as well as two Java classes that provide class definitions. You can get information about the DAO objects from the on-line help provided with the Microsoft Visual C++ compiler package.

Design Issues: Mapping C++ Objects to Java Objects

The Microsoft MFC DAO classes are already divided into discrete functional units. Consequently, defining a mapping from C++ to equivalent Java classes is straightforward. Wrapping a C API is slightly more complex due to the fact that there aren't necessarily any inherent objects within a C API. Your options when mapping a C API are basically to either create objects with methods that provide an interface to several logically related C functions, or to simply create a class that exposes static methods that wrap the C functions.
The DAOLayer project wraps the MFC CDaoDatabase class and the CDaoRecordset class. The Java classes dao.Database and dao.Recordset wrap the CDaoDatabase class and the CDaoRecordset class respectively. Only a few of the C++ member functions are exposed within the Java wrapper classes; dao.Database provides open and close methods, and dao.Recordset provides open, close, navigation, and field value retrieval functions. An implementation that was more full would provide additional functionality, such as write access, to field values in a Recordset.

Defining the Database Java Wrapper Object

The dao.Database class is a wrapper for the MFC CDaoDatabase class. It provides the ability to open databases that may be used to retrieve recordsets using the Recordset object.
The dao.Database object is, in some respects, the most important class in the DAOLayer dao package. It contains Java code to load the library containing the native methods that implement the dao.Database functionality. The excerpt that loads the DAOLayer dynamic link library follows:
static {
The dao.Database object also contains two very important static native methods, initDAO() and termDAO(). The MFC DAO library must be initialized using explicit calls to AfxDaoInit() and AfxDaoTerm() functions when it is used within a dynamic link library; the native methods initDAO() and termDAO() wrap those functions respectively. It's the responsibility of the Java code that uses the dao.* package to call dao.Database.initDAO() before using any classes in the package, and call dao.Database.termDAO() when finished.
The following section describing the Recordset object includes more details about wrapping C++ classes with Java objects.

Defining the Recordset Java Wrapper Object

The DAO Recordset object provides the ability to access a set of records from a DAO database. The MFC DAO CDaoRecordset class has numerous member functions that can be used to retrieve a set of records using an SQL statement, navigate through the records, and retrieve and update field values in records. For the sake of simplicity, the implementation of the Recordset provides only a few of the functions from the C++ class. It should be fairly simple for you to extend the class to add more functionality.
The definition of the dao.Recordset object follows:
package dao;

public class Recordset {
    public native void open(String strSQL) throws DaoException;
    public native void close() throws DaoException;
    public native boolean isEOF() throws DaoException;
    public native void moveFirst() throws DaoException;
    public native void moveNext() throws DaoException;
    public native String getFieldValue(String strField) throws DaoException;

    protected dao.Database db;
    protected int pRec; // Pointer to CDaoRecordset instance
    protected native void allocRecObject();
    protected native void deleteRecObject();

    public Recordset(dao.Database db) {
        this.db = db;

    protected void finalize() throws Throwable {
Each of the public methods defined in the dao.Recordset class corresponds to a member function in the C++ class. Literally the only difference is that the names of the methods are prefixed with a lowercase letter, in accordance to standard Java method capitalization conventions, as opposed to uppercase, as per the C++ class member definitions. The effects of the functions correspond to the equivalently named C++ member functions. Most of the native methods are simple dispatching functions that call C++ functions.
At this point you may be wondering how the native methods call C++ functions. The private instance variable pRec is used to store a pointer to a CDaoRecordset object. The native method casts the pRec from an integer (which is a C long) back to a CDaoRecordset pointer, then calls the desired CDaoRecordset member function. The implementation of the moveNext() native method follows:
void dao_Recordset_moveNext(struct Hdao_Recordset* me)
    try {
    } catch (CDaoException* pe) {
Notice that the name of the native method, dao_Recordset_moveNext(), includes the name of the package as a prefix. If the Recordset class were defined in a web.db.dao package, the Recordset_moveNext() method would have the prefix web_db_dao_.

The getRecPtr() function is a useful utility function that returns a pointer to a CDaoRecordset object, given a handle to a Java Recordset object. It is implemented as follows:
CDaoRecordset* getRecPtr(struct Hdao_Recordset* daoRec)
    return ((CDaoRecordset*)
        ((struct Classdao_Recordset*)unhand(daoRec)->pRec));
The complementary set function, setRecPtr(), includes the assertion that a Java int member variable, which is defined in the Java C structure as a long, is the same length in bytes as a CDaoRecordset pointer. The function is implemented as
void setRecPtr(struct Hdao_Recordset* daoRec, CDaoRecordset* pRec)
    ASSERT(sizeof(long) == sizeof(CDaoRecordset*));
    ((struct Classdao_Recordset*)unhand(daoRec))->pRec = (long)pRec;
The get and set functions are used as simple convenience functions that obviate the need to maintain complicated sequences of casts and calls to the unhand() function.
As previously indicated, the java class includes a member that is used to store a pointer to a CDaoRecordset instance. One of the issues in mapping Java classes to C++ classes is the lifetime of the objects. You'll notice a call to a protected allocRecObject() function in the constructor of dao.Recordset. That function is used to create a CDaoRecordset instance and connect it to pRec. The class finalizer includes a call to the matching delete function, deleteRecObject(), that deletes the C++ object when the lifetime of the Java object ends.
Passing C++ Exceptions to Java
In the implementation of the Recordset.moveNext() method, you may notice that the call to the C++ CDaoRecordset::MoveNext() member function is contained within a C++ try/catch block. The MoveNext() function may raise a C++ exception of type CDaoException. To maintain the semantics of the class, from the Java perspective, with regards to the exception behavior, the C++ exception is converted to a Java exception by the throwTranslatedDaoException() function. The throwTranslatedDaoException() function is implemented as follows:
void throwTranslatedDaoException(CDaoException* pe)
    CString strErr;
    CDaoErrorInfo *pErr = pe->m_pErrorInfo;

    strErr.Format("%s (%ld) - %s", pErr->m_strSource, pErr->m_lErrorCode,
    SignalError(EE(), "dao/DaoException", (char*)(LpcSTR)strErr);
The function takes a pointer to a CDaoException object. When an exception occurs, a pointer to the exception is acquired in the catch block that brackets the call to CDaoRecordset::MoveNext(). Using the convenient MFC CString class, the function creates a readable string that represents the CDaoException that occurred. After the string is created, an exception is raised using the SignalError() Java API function.
For the throwTranslatedDaoException() function, a dao.DaoException is thrown. The Java dao.DaoException class, defined in, is a straightforward subclass of the java.lang.Exception class.
After the Java exception is thrown, the C++ exception is deleted. For the CDaoException class, the CDaoException::Delete() member function must be called. Other C++ exceptions may be deleted by the standard C++ delete operator. The Java interpreter doesn't detect the occurrence of the Java exception until the native method returns, at which point the Java exception object is available to the Java code that called the native method. 


Post a Comment