Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 5
Once again, from the beginning: When a sales representative starts Aphid, the Aphid.Representative object must request the Beetle.Employee object to which it is connected. All it has is a cookie. Because Beetle.Company is the owner of Beetle.Employee, Aphid.Representative passes the cookie to Beetle.Company. Beetle.Company must access the business logic layer to complete the request, so that is where we require our first native method. The code
on the Java side looks like this:
native private Employee loadEmployee( long nEmployeeSS );
Of course, we want to hide the fact that the cookie is really the employee's social security number encoded as a long integer
from Aphid. We make the method private so that Aphid.Representative can't call it directly -- it has to go through the public method Employee lookupEmployee( String cookie ).
Notice that the Java method does not include an implementation; the implementation appears in the C++ module BeetleJNI.dll. The function must be declared precisely as follows so that JNI can match it to the proper native method declaration:
extern "C"
{
_BEETLEJNI_EXPORT_ jobject JNICALL Java_Beetle_Company_loadEmployee(
JNIEnv *env,
jobject obj,
jlong nEmployeeSSN);
}
A little explanation is in order here: The function name identifies itself as JNI native code (Java_) and identifies the package (Beetle_), class (Company_), and method (loadEmployee). It takes a JNI environment object (JNIEnv *env) (which we will use to communicate with the VM), a reference to the Company instance (jobject obj), and the long parameter that we declared in Java (jlong nEmployeeSSN). Because we declared that the native method returns an Employee object in Java, the C++ function returns jobject. The extern "C" and JNICALL modifiers ensure that the name is not mangled and that the proper calling convention is used. And of course, _BEETLEJNI_EXPORT_ is a macro that instructs the compiler to export the function from the DLL. (See Sidebar 1 for more information about native method naming.)
To make sure that everything is working properly thus far, we create an empty stub for the native code -- just enough to hold
a breakpoint. Then we implement the Java code to invoke our native method. (Calling a native method in Java is exactly the
same as calling a Java method.) We set up the debugger to run BeetleJNI.dll by way of Aphid. In Visual C++, that can be accomplished in the Debug panel of the Project Settings dialog by specifying
"D:\JBuilder35\jdk1.2.2\bin\javaw.exe" as the "Executable for debug session" and "-classpath D:\Dev\Laijni\Java\classes Aphid.App"
as the program arguments. (I have the JDK installed in "D:\JBuilder35\jdk1.2.2" and the source code starting in "D:\Dev\Laijni";
your configuration may differ.) We can then set the breakpoint and run the program, and we shouldn't continue until the breakpoint
is hit, confirming that we have correctly connected the modules.
With our framework firmly in place, we can start hanging code from the rafters. We must complete the first native method before
moving on to the next, which requires that we modify code in all four modules on each iteration. That sequential movement
also allows us to compile and test a stable build before moving on to the next change. The new development in Aphid and BeetleJNI.dll is not the risky part -- it's the legacy code that's perilous ground.