附录A 使用非JAVA代码
JAVA语言及其标准API(应用程序编程接口)应付应用程序的编写已绰绰有余。但在某些情况下,还是必须使用非JAVA编码。例如,我们有时要访问操作系统的专用特性,与特殊的硬件设备打交道,重复使用现有的非Java接口,或者要使用“对时间敏感”的代码段,等等。与非Java代码的沟通要求获得编译器和“虚拟机”的专门支持,并需附加的工具将Java代码映射成非Java代码(也有一个简单方法:在第15章的“一个Web应用”小节中,有个例子解释了如何利用标准输入输出同非Java代码连接)。目前,不同的开发商为我们提供了不同的方案:Java
1.1有“Java固有接口”(Java Native Interface,JNI),网景提出了自己的“Java运行期接口”(Java
Runtime Interface)计划,而微软提供了J/Direct、“本源接口”(Raw Native
Interface,RNI)以及Java/COM集成方案。
各开发商在这个问题上所持的不同态度对程序员是非常不利的。若Java应用必须调用固有方法,则程序员或许要实现固有方法的不同版本——具体由应用程序运行的平台决定。程序员也许实际需要不同版本的Java代码,以及不同的Java虚拟机。
另一个方案是CORBA(通用对象请求代理结构),这是由OMG(对象管理组,一家非赢利性的公司协会)开发的一种集成技术。CORBA并非任何语言的一部分,只是实现通用通信总线及服务的一种规范。利用它可在由不同语言实现的对象之间实现“相互操作”的能力。这种通信总线的名字叫作ORB(对象请求代理),是由其他开发商实现的一种产品,但并不属于Java语言规范的一部分。
本附录将对JNI,J/DIRECT,RNI,JAVA/COM集成和CORBA进行概述。但不会作更深层次的探讨,甚至有时还假定读者已对相关的概念和技术有了一定程度的认识。但到最后,大家应该能够自行比较不同的方法,并根据自己要解决的问题挑选出最恰当的一种。
A.1 Java固有接口
JNI是一种包容极广的编程接口,允许我们从Java应用程序里调用固有方法。它是在Java
1.1里新增的,维持着与Java 1.0的相应特性——“固有方法接口”(NMI)——某种程度的兼容。NMI设计上一些特点使其未获所有虚拟机的支持。考虑到这个原因,Java语言将来的版本可能不再提供对NMI的支持,这儿也不准备讨论它。
目前,JNI只能与用C或C++写成的固有方法打交道。利用JNI,我们的固有方法可以:
■创建、检查及更新Java对象(包括数组和字串)
■调用Java方法
■俘获和丢弃“异常”
■装载类并获取类信息
■进行运行期类型检查
所以,原来在Java中能对类及对象做的几乎所有事情在固有方法中同样可以做到。
A.1.1 调用固有方法
我们先从一个简单的例子开始:一个Java程序调用固有方法,后者再调用Win32的API函数MessageBox(),显示出一个图形化的文本框。这个例子稍后也会与J/Direct一志使用。若您的平台不是Win32,只需将包含了下述内容的C头:
#include <windows.h>
替换成:
#include <stdio.h>
并将对MessageBox()的调用换成调用printf()即可。
第一步是写出对固有方法及它的自变量进行声明的Java代码:
class ShowMsgBox { public static void main(String [] args) { ShowMsgBox app = new ShowMsgBox(); app.ShowMessage("Generated with JNI"); } private native void ShowMessage(String msg); static { System.loadLibrary("MsgImpl"); } }
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class ShowMsgBox */ #ifndef _Included_ShowMsgBox #define _Included_ShowMsgBox #ifdef __cplusplus extern "C" { #endif /* * Class: ShowMsgBox * Method: ShowMessage * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_ShowMsgBox_ShowMessage (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif
#include <windows.h> #include "ShowMsgBox.h" BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void** lpReserved) { return TRUE; } JNIEXPORT void JNICALL Java_ShowMsgBox_ShowMessage(JNIEnv * jEnv, jobject this, jstring jMsg) { const char * msg; msg = (*jEnv)->GetStringUTFChars(jEnv, jMsg,0); MessageBox(HWND_DESKTOP, msg, "Thinking in Java: JNI", MB_OK | MB_ICONEXCLAMATION); (*jEnv)->ReleaseStringUTFChars(jEnv, jMsg,msg); }
class MyJavaClass { public void divByTwo() { aValue /= 2; } public int aValue; } public class UseObjects { public static void main(String [] args) { UseObjects app = new UseObjects(); MyJavaClass anObj = new MyJavaClass(); anObj.aValue = 2; app.changeObject(anObj); System.out.println("Java: " + anObj.aValue); } private native void changeObject(MyJavaClass obj); static { System.loadLibrary("UseObjImpl"); } }
JNIEXPORT void JNICALL Java_UseObjects_changeObject( JNIEnv * env, jobject jThis, jobject obj) { jclass cls; jfieldID fid; jmethodID mid; int value; cls = env->GetObjectClass(obj); fid = env->GetFieldID(cls, "aValue", "I"); mid = env->GetMethodID(cls, "divByTwo", "()V"); value = env->GetIntField(obj, fid); printf("Native: %d\n", value); env->SetIntField(obj, fid, 6); env->CallVoidMethod(obj, mid); value = env->GetIntField(obj, fid); printf("Native: %d\n", value); }
public class ShowMsgBox { public static void main(String args[]) throws UnsatisfiedLinkError { MessageBox(0, "Created by the MessageBox() Win32 func", "Thinking in Java", 0); } /** @dll.import("USER32") */ private static native int MessageBox(int hwndOwner, String text, String title, int fuStyle); }
public class Aliasing { public static void main(String args[]) throws UnsatisfiedLinkError { FinestraDiMessaggio(0, "Created by the MessageBox() Win32 func", "Thinking in Java", 0); } /** @dll.import("USER32", entrypoint="MessageBox") */ private static native int FinestraDiMessaggio(int hwndOwner, String text, String title, int fuStyle); }
public class ByOrdinal { public static void main(String args[]) throws UnsatisfiedLinkError { int j=3, k=9; System.out.println("Result of DLL function:" + Add(j,k)); } /** @dll.import("MYMATH", entrypoint = "#3") */ private static native int Add(int op1,int op2); }
/** @dll.import("USER32") */ class MyUser32Access { public static native int MessageBox(int hwndOwner, String text, String title, int fuStyle); public native static boolean MessageBeep(int uType); } public class WholeClass { public static void main(String args[]) throws UnsatisfiedLinkError { MyUser32Access.MessageBeep(4); MyUser32Access.MessageBox(0, "Created by the MessageBox() Win32 func", "Thinking in Java", 0); } }
import com.ms.win32.*; public class UseWin32Package { public static void main(String args[]) { try { User32.MessageBeep( winm.MB_ICONEXCLAMATION); User32.MessageBox(0, "Created by the MessageBox() Win32 func", "Thinking in Java", winm.MB_OKCANCEL | winm.MB_ICONEXCLAMATION); } catch(UnsatisfiedLinkError e) { System.out.println("Can’t link Win32 API"); System.out.println(e); } } }
import com.ms.dll.*; import com.ms.win32.*; class EnumWindowsProc extends Callback { public boolean callback(int hwnd, int lparam) { StringBuffer text = new StringBuffer(50); User32.GetWindowText( hwnd, text, text.capacity()+1); if(text.length() != 0) System.out.println(text); return true; // to continue enumeration. } } public class ShowCallback { public static void main(String args[]) throws InterruptedException { boolean ok = User32.EnumWindows( new EnumWindowsProc(), 0); if(!ok) System.err.println("EnumWindows failed."); Thread.currentThread().sleep(3000); } }
/* DO NOT EDIT - automatically generated by msjavah */ #include <native.h> #pragma warning(disable:4510) #pragma warning(disable:4512) #pragma warning(disable:4610) struct Classjava_lang_String; #define Hjava_lang_String Classjava_lang_String /* Header for class ShowMsgBox */ #ifndef _Included_ShowMsgBox #define _Included_ShowMsgBox #define HShowMsgBox ClassShowMsgBox typedef struct ClassShowMsgBox { #include <pshpack4.h> long MSReserved; #include <poppack.h> } ClassShowMsgBox; #ifdef __cplusplus extern "C" { #endif __declspec(dllexport) void __cdecl ShowMsgBox_ShowMessage (struct HShowMsgBox *, struct Hjava_lang_String *); #ifdef __cplusplus } #endif #endif /* _Included_ShowMsgBox */ #pragma warning(default:4510) #pragma warning(default:4512) #pragma warning(default:4610)
public class Adder { private int addend; private int result; public void setAddend(int a) { addend = a; } public int getAddend() { return addend; } public int getResult() { return result; } public void sum() { result += addend; } public void clear() { result = 0; addend = 0; } }
Dim Adder As Object Private Sub Form_Load() Set Adder = CreateObject("JavaAdder.Adder.1") Addend.Text = Adder.getAddend Result.Caption = Adder.getResult End Sub Private Sub SumBtn_Click() Adder.setAddend (Addend.Text) Adder.Sum Result.Caption = Adder.getResult End Sub Private Sub ClearBtn_Click() Adder.Clear Addend.Text = Adder.getAddend Result.Caption = Adder.getResult End Sub
import javaadder.*; public class JavaClient { public static void main(String [] args) { Adder_DispatchDefault iAdder = (Adder_DispatchDefault) new Adder(); iAdder.setAddend(3); iAdder.sum(); iAdder.sum(); iAdder.sum(); System.out.println(iAdder.getResult()); } }
module RemoteTime { interface ExactTime { string getTime(); }; };
import RemoteTime.*; import org.omg.CosNaming.*; import org.omg.CosNaming.NamingContextPackage.*; import org.omg.CORBA.*; import java.util.*; import java.text.*; // Server object implementation class ExactTimeServer extends _ExactTimeImplBase{ public String getTime(){ return DateFormat. getTimeInstance(DateFormat.FULL). format(new Date( System.currentTimeMillis())); } } // Remote application implementation public class RemoteTimeServer { public static void main(String args[]) { try { // ORB creation and initialization: ORB orb = ORB.init(args, null); // Create the server object and register it: ExactTimeServer timeServerObjRef = new ExactTimeServer(); orb.connect(timeServerObjRef); // Get the root naming context: org.omg.CORBA.Object objRef = orb.resolve_initial_references( "NameService"); NamingContext ncRef = NamingContextHelper.narrow(objRef); // Assign a string name to the // object reference (binding): NameComponent nc = new NameComponent("ExactTime", ""); NameComponent path[] = {nc}; ncRef.rebind(path, timeServerObjRef); // Wait for client requests: java.lang.Object sync = new java.lang.Object(); synchronized(sync){ sync.wait(); } } catch (Exception e) { System.out.println( "Remote Time server error: " + e); e.printStackTrace(System.out); } } }
import RemoteTime.*; import org.omg.CosNaming.*; import org.omg.CORBA.*; public class RemoteTimeClient { public static void main(String args[]) { try { // ORB creation and initialization: ORB orb = ORB.init(args, null); // Get the root naming context: org.omg.CORBA.Object objRef = orb.resolve_initial_references( "NameService"); NamingContext ncRef = NamingContextHelper.narrow(objRef); // Get (resolve) the stringified object // reference for the time server: NameComponent nc = new NameComponent("ExactTime", ""); NameComponent path[] = {nc}; ExactTime timeObjRef = ExactTimeHelper.narrow( ncRef.resolve(path)); // Make requests to the server object: String exactTime = timeObjRef.getTime(); System.out.println(exactTime); } catch (Exception e) { System.out.println( "Remote Time server error: " + e); e.printStackTrace(System.out); } } }