JSP可以控制硬件吗?比如对IC卡的读写操作等……

wanwangzhiwang 2001-10-10 09:57:32
...全文
262 点赞 收藏 12
写回复
12 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
wanwangzhiwang 2001-10-15
谢谢萧云!
回复
wanwangzhiwang 2001-10-12
谁给我中文资料我给谁分!决不食言!马上!
回复
feiyangf 2001-10-12
好吧,再贴一页。

本文为在 32 位 Windows 平台上实现 Java 本地方法提供了实用的示例、步骤和准则。这些示例包括传递和返回常用的数据类型。
本文中的示例使用 Sun Microsystems 公司创建的 Java Development Kit (JDK) 版本 1.1.6 和 Java 本地接口 (JNI) 规范。用 C 语言编写的本地代码是用 Microsoft Visual C++ 编译器编译生成的。
简介
本文提供调用本地 C 代码的 Java 代码示例,包括传递和返回某些常用的数据类型。本地方法包含在特定于平台的可执行文件中。就本文中的示例而言,本地方法包含在 Windows 32 位动态链接库 (DLL) 中。
不过我要提醒您,对 Java 外部的调用通常不能移植到其他平台上,在 applet 中还可能引发安全异常。实现本地代码将使您的 Java 应用程序无法通过 100% 纯 Java 测试。但是,如果必须执行本地调用,则要考虑几个准则:
1. 将您的所有本地方法都封装在单个类中,这个类调用单个 DLL。对于每种目标操作系统,都可以用特定于适当平台的版本替换这个 DLL。这样就可以将本地代码的影响减至最小,并有助于将以后所需的移植问题包含在内。
2. 本地方法要简单。尽量将您的 DLL 对任何第三方(包括 Microsoft)运行时 DLL 的依赖减到最小。使您的本地方法尽量独立,以将加载您的 DLL 和应用程序所需的开销减到最小。如果需要运行时 DLL,必须随应用程序一起提供它们。
Java 调用 C
对于调用 C 函数的 Java 方法,必须在 Java 类中声明一个本地方法。在本部分的所有示例中,我们将创建一个名为 MyNative 的类,并逐步在其中加入新的功能。这强调了一种思想,即将本地方法集中在单个类中,以便将以后所需的移植工作减到最少。
示例 1 -- 传递参数
在第一个示例中,我们将三个常用参数类型传递给本地函数:String、int 和 boolean。本例说明在本地 C 代码中如何引用这些参数。

public class MyNative
{
public void showParms( String s, int i, boolean b )
{
showParms0( s, i , b );
}

private native void showParms0( String s, int i, boolean b );

static
{
System.loadLibrary( "MyNative" );
}
}
请注意,本地方法被声明为专用的,并创建了一个包装方法用于公用目的。这进一步将本地方法同代码的其余部分隔离开来,从而允许针对所需的平台对它进行优化。static 子句加载包含本地方法实现的 DLL。
下一步是生成 C 代码来实现 showParms0 方法。此方法的 C 函数原型是通过对 .class 文件使用 javah 实用程序来创建的,而 .class 文件是通过编译 MyNative.java 文件生成的。这个实用程序可在 JDK 中找到。下面是 javah 的用法:
javac MyNative.java(将 .java 编译为 .class)
javah -jni
MyNative(生成 .h 文件)
这将生成一个 MyNative.h 文件,其中包含一个本地方法原型,如下所示:

/*
* Class: MyNative
* Method: showParms0
* Signature: (Ljava/lang/String;IZ)V
*/
JNIEXPORT void JNICALL Java_MyNative_showParms0
(JNIEnv *, jobject, jstring, jint, jboolean);
第一个参数是调用 JNI 方法时使用的 JNI Environment 指针。第二个参数是指向在此 Java 代码中实例化的 Java 对象 MyNative 的一个句柄。其他参数是方法本身的参数。请注意,MyNative.h 包括头文件 jni.h。jni.h 包含 JNI API 和变量类型(包括jobject、jstring、jint、jboolean,等等)的原型和其他声明。
本地方法是在文件 MyNative.c 中用 C 语言实现的:

#include <stdio.h>
#include "MyNative.h"
JNIEXPORT void JNICALL Java_MyNative_showParms0
(JNIEnv *env, jobject obj, jstring s, jint i, jboolean b)
{
const char* szStr = (*env)->GetStringUTFChars( env, s, 0 );
printf( "String = [%s]\n", szStr );
printf( "int = %d\n", i );
printf( "boolean = %s\n", (b==JNI_TRUE ? "true" : "false") );
(*env)->ReleaseStringUTFChars( env, s, szStr );
}
JNI API,GetStringUTFChars,用来根据 Java 字符串或 jstring 参数创建 C 字符串。这是必需的,因为在本地代码中不能直接读取 Java 字符串,而必须将其转换为 C 字符串或 Unicode。有关转换 Java 字符串的详细信息,请参阅标题为 NLS Strings and JNI 的一篇论文。但是,jboolean 和 jint 值可以直接使用。
MyNative.dll 是通过编译 C 源文件创建的。下面的编译语句使用 Microsoft Visual C++ 编译器:
cl -Ic:\jdk1.1.6\include -Ic:\jdk1.1.6\include\win32 -LD MyNative.c
-FeMyNative.dll
其中 c:\jdk1.1.6 是 JDK 的安装路径。
MyNative.dll 已创建好,现在就可将其用于 MyNative 类了。可以这样测试这个本地方法:在 MyNative 类中创建一个 main 方法来调用 showParms 方法,如下所示:

public static void main( String[] args )
{
MyNative obj = new MyNative();
obj.showParms( "Hello", 23, true );
obj.showParms( "World", 34, false );
}
当运行这个 Java 应用程序时,请确保 MyNative.dll 位于 Windows 的 PATH 环境变量所指定的路径中或当前目录下。当执行此 Java 程序时,如果未找到这个 DLL,您可能会看到以下的消息:
java MyNative
Can't find class MyNative
这是因为 static 子句无法加载这个 DLL,所以在初始化 MyNative 类时引发异常。Java 解释器处理这个异常,并报告一个一般错误,指出找不到这个类。如果用 -verbose 命令行选项运行解释器,您将看到它因找不到这个 DLL 而加载 UnsatisfiedLinkError 异常。
如果此 Java 程序完成运行,就会输出以下内容:
java MyNative
String = [Hello]
int = 23

boolean = true
String = [World]
int
= 34
boolean = false
示例 2 -- 返回一个值
本例将说明如何在本地方法中实现返回代码。将这个方法添加到 MyNative 类中,这个类现在变为以下形式:

public class MyNative
{
public void showParms( String s, int i, boolean b )
{
showParms0( s, i , b );
}
public int hypotenuse( int a, int b )
{
return hyptenuse0( a, b );
}

private native void showParms0( String s, int i, boolean b );
private native int hypotenuse0( int a, int b );

static
{
System.loadLibrary( "MyNative" );
}

/* 测试本地方法 */
public static void main( String[] args )
{
MyNative obj = new MyNative();
System.out.println( obj.hypotenuse(3,4) );
System.out.println( obj.hypotenuse(9,12) );
}
}
公用的 hypotenuse 方法调用本地方法 hypotenuse0 来根据传递的参数计算值,并将结果作为一个整数返回。这个新本地方法的原型是使用 javah 生成的。请注意,每次运行这个实用程序时,它将自动覆盖当前目录中的 MyNative.h。按以下方式执行 javah:
javah -jni MyNative
生成的 MyNative.h 现在包含 hypotenuse0 原型,如下所示:

/*
* Class: MyNative
* Method: hypotenuse0
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_MyNative_hypotenuse0
(JNIEnv *, jobject, jint, jint);
该方法是在 MyNative.c 源文件中实现的,如下所示:

#include <stdio.h>
#include <math.h>
#include "MyNative.h"

JNIEXPORT void JNICALL Java_MyNative_showParms0
(JNIEnv *env, jobject obj, jstring s, jint i, jboolean b)
{
const char* szStr = (*env)->GetStringUTFChars( env, s, 0 );
printf( "String = [%s]\n", szStr );
printf( "int = %d\n", i );
printf( "boolean = %s\n", (b==JNI_TRUE ? "true" : "false") );
(*env)->ReleaseStringUTFChars( env, s, szStr );
}

JNIEXPORT jint JNICALL Java_MyNative_hypotenuse0
(JNIEnv *env, jobject obj, jint a, jint b)
{
int rtn = (int)sqrt( (double)( (a*a) + (b*b) ) );
return (jint)rtn;
}
再次请注意,jint 和 int 值是可互换的。使用相同的编译语句重新编译这个 DLL:
cl -Ic:\jdk1.1.6\include -Ic:\jdk1.1.6\include\win32 -LD MyNative.c
-FeMyNative.dll
现在执行 java MyNative 将输出 5 和 15 作为斜边的值。
示例 3 -- 静态方法
您可能在上面的示例中已经注意到,实例化的 MyNative 对象是没必要的。实用方法通常不需要实际的对象,通常都将它们创建为静态方法。本例说明如何用一个静态方法实现上面的示例。更改 MyNative.java 中的方法签名,以使它们成为静态方法:

public static int hypotenuse( int a, int b )
{
return hypotenuse0(a,b);
}
...
private static native int hypotenuse0( int a, int b );
现在运行 javah 为 hypotenuse0 创建一个新原型,生成的原型如下所示:

/*
* Class: MyNative
* Method: hypotenuse0
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_MyNative_hypotenuse0
(JNIEnv *, jclass, jint, jint);
C 源代码中的方法签名变了,但代码还保持原样:

JNIEXPORT jint JNICALL Java_MyNative_hypotenuse0
(JNIEnv *env, jclass cls, jint a, jint b)
{
int rtn = (int)sqrt( (double)( (a*a) + (b*b) ) );
return (jint)rtn;
}
本质上,jobject 参数已变为 jclass 参数。此参数是指向 MyNative.class 的一个句柄。main 方法可更改为以下形式:

public static void main( String[] args )
{
System.out.println( MyNative.hypotenuse( 3, 4 ) );
System.out.println( MyNative.hypotenuse( 9, 12 ) );
}

因为方法是静态的,所以调用它不需要实例化 MyNative 对象。本文后面的示例将使用静态方法。
示例 4 -- 传递数组
本例说明如何传递数组型参数。本例使用一个基本类型,boolean,并将更改数组元素。下一个示例将访问 String(非基本类型)数组。将下面的方法添加到 MyNative.java 源代码中:

public static void setArray( boolean[] ba )
{
for( int i=0; i < ba.length; i++ )
ba[i] = true;
setArray0( ba );
}
...
private static native void setArray0( boolean[] ba );
在本例中,布尔型数组被初始化为 true,本地方法将把特定的元素设置为 false。同时,在 Java 源代码中,我们可以更改 main 以使其包含测试代码:

boolean[] ba = new boolean[5];
MyNative.setArray( ba );
for( int i=0; i < ba.length; i++ )
System.out.println( ba[i] );
在编译源代码并执行 javah 以后,MyNative.h 头文件包含以下的原型:

/*
* Class: MyNative
* Method: setArray0
* Signature: ([Z)V
*/
JNIEXPORT void JNICALL Java_MyNative_setArray0
(JNIEnv *, jclass, jbooleanArray);
请注意,布尔型数组是作为单个名为 jbooleanArray 的类型创建的。基本类型有它们自已的数组类型,如 jintArray 和 jcharArray。非基本类型的数组使用 jobjectArray 类型。下一个示例中包括一个 jobjectArray。这个布尔数组的数组元素是通过 JNI 方法 GetBooleanArrayElements 来访问的。针对每种基本类型都有等价的方法。这个本地方法是如下实现的:

JNIEXPORT void JNICALL Java_MyNative_setArray0
(JNIEnv *env, jclass cls, jbooleanArray ba)
{
jboolean* pba = (*env)->GetBooleanArrayElements( env, ba, 0 );
jsize len = (*env)->GetArrayLength(env, ba);
int i=0;
// 更改偶数数组元素
for( i=0; i < len; i+=2 )
pba[i] = JNI_FALSE;
(*env)->ReleaseBooleanArrayElements( env, ba, pba, 0 );
}
指向布尔型数组的指针可以使用 GetBooleanArrayElements 获得。数组大小可以用 GetArrayLength 方法获得。使用 ReleaseBooleanArrayElements 方法释放数组。现在就可以读取和修改数组元素的值了。jsize 声明等价于 jint(要查看它的定义,请参阅 JDK 的 include 目录下的 jni.h 头文件)。
示例 5 -- 传递 Java String 数组
本例将通过最常用的非基本类型,Java String,说明如何访问非基本对象的数组。字符串数组被传递给本地方法,而本地方法只是将它们显示到控制台上。 MyNative 类定义中添加了以下几个方法:

public static void showStrings( String[] sa )
{
showStrings0( sa );
}
private static void showStrings0( String[] sa );
并在 main 方法中添加了两行进行测试:

String[] sa = new String[] { "Hello,", "world!", "JNI", "is", "fun." };
MyNative.showStrings( sa );
本地方法分别访问每个元素,其实现如下所示。

JNIEXPORT void JNICALL Java_MyNative_showStrings0
(JNIEnv *env, jclass cls, jobjectArray sa)
{
int len = (*env)->GetArrayLength( env, sa );
int i=0;
for( i=0; i < len; i++ )
{
jobject obj = (*env)->GetObjectArrayElement(env, sa, i);
jstring str = (jstring)obj;
const char* szStr = (*env)->GetStringUTFChars( env, str, 0 );
printf( "%s ", szStr );
(*env)->ReleaseStringUTFChars( env, str, szStr );
}
printf( "\n" );
}
数组元素可以通过 GetObjectArrayElement 访问。在本例中,我们知道返回值是 jstring 类型,所以可以安全地将它从 jobject 类型转换为 jstring 类型。字符串是通过前面讨论过的方法打印的。有关在 Windows 中处理 Java 字符串的信息,请参阅标题为 NLS Strings and JNI 的一篇论文。
示例 6 -- 返回 Java String 数组
最后一个示例说明如何在本地代码中创建一个字符串数组并将它返回给 Java 调用者。MyNative.java 中添加了以下几个方法:

public static String[] getStrings()
{
return getStrings0();
}
private static native String[] getStrings0();
更改 main 以使 showStrings 将 getStrings 的输出显示出来:

MyNative.showStrings( MyNative.getStrings() );
实现的本地方法返回五个字符串。

JNIEXPORT jobjectArray JNICALL Java_MyNative_getStrings0
(JNIEnv *env, jclass cls)
{
jstring str;
jobjectArray args = 0;
jsize len = 5;
char* sa[] = { "Hello,", "world!", "JNI", "is", "fun" };
int i=0;
args = (*env)->NewObjectArray(env, len, (*env)->FindClass(env, "java/lang/String"), 0);
for( i=0; i < len; i++ )
{
str = (*env)->NewStringUTF( env, sa[i] );
(*env)->SetObjectArrayElement(env, args, i, str);
}
return args;
}
字符串数组是通过调用 NewObjectArray 创建的,同时传递了 String 类和数组长度两个参数。Java String 是使用 NewStringUTF 创建的。String 元素是使用 SetObjectArrayElement 存入数组中的。
调试
现在您已经为您的应用程序创建了一个本地 DLL,但在调试时还要牢记以下几点。如果使用 Java 调试器 java_g.exe,则还需要创建 DLL 的一个“调试”版本。这只是表示必须创建同名但带有一个 _g 后缀的 DLL 版本。就 MyNative.dll 而言,使用 java_g.exe 要求在 Windows 的 PATH 环境指定的路径中有一个 MyNative_g.dll 文件。在大多数情况下,这个 DLL 可以通过将原文件重命名或复制为其名称带缀 _g 的文件。
现在,Java 调试器不允许您进入本地代码,但您可以在 Java 环境外使用 C 调试器(如 Microsoft Visual C++)调试本地方法。首先将源文件导入一个项目中。将编译设置调整为在编译时将 include 目录包括在内:
c:\jdk1.1.6\include;c:\jdk1.1.6\include\win32
将配置设置为以调试模式编译 DLL。在 Project Settings 中的 Debug 下,将可执行文件设置为 java.exe(或者 java_g.exe,但要确保您生成了一个 _g.dll 文件)。程序参数包括包含 main 的类名。如果在 DLL 中设置了断点,则当调用本地方法时,执行将在适当的地方停止。
下面是设置一个 Visual C++ 6.0 项目来调试本地方法的步骤。
1. 在 Visual C++ 中创建一个 Win32 DLL 项目,并将 .c 和 .h 文件添加到这个项目中。


2. 在 Tools 下拉式菜单的 Options 设置下设置 JDK 的 include 目录。下面的对话框显示了这些目录。

3. 选择 Build 下拉式菜单下的 Build MyNative.dll 来建立这个项目。确保将项目的活动配置设置为调试(这通常是缺省值)。
4. 在 Project Settings 下,设置 Debug 选项卡来调用适当的 Java 解释器,如下所示:

当执行这个程序时,忽略“在 java.exe 中找不到任何调试信息”的消息。当调用本地方法时,在 C 代码中设置的任何断点将在适当的地方停止 Java 程序的执行。
其他信息
JNI 方法和 C++
上面这些示例说明了如何在 C 源文件中使用 JNI 方法。如果使用 C++,则请将相应方法的格式从:
(*env)->JNIMethod( env, .... );
更改为:
env->JNIMethod( ... );
在 C++ 中,JNI 函数被看作是 JNIEnv 类的成员方法。
字符串和国家语言支持
本文中使用的技术用 UTF 方法来转换字符串。使用这些方法只是为了方便起见,如果应用程序需要国家语言支持 (NLS),则不能使用这些方法。有关在 Windows 和 NLS 环境中处理 Java 字符串正确方法,请参标题为 NLS Strings and JNI 的一篇论文。
小结
本文提供的示例用最常用的数据类据(如 jint 和 jstring)说明了如何实现本地方法,并讨论了 Windows 特定的几个问题,如显示字符串。本文提供的示例并未包括全部 JNI,JNI 还包括其他参数类型,如 jfloat、jdouble、jshort、jbyte 和 jfieldID,以及用来处理这些类型的方法。有关这个主题的详细信息,请参阅 Sun Microsystems 提供的 Java 本地接口规范。
回复
wanwangzhiwang 2001-10-11
谁有中文资料
回复
wanwangzhiwang 2001-10-10
楼上这位老兄:
JNI是什么?类吗?如何使用?拜托您说清楚点,谢谢了!
回复
feiyangf 2001-10-10
当然可以啊,呵呵。
我知道你的意思,你想用IC这个东西完成用户的认证和收费,但是我得提醒你,你的用户群会因此受到很大的限制:因为首先他们还得安装IC卡读卡器(你的客户可就不瘦啦,呵呵,你的WEB优势受到挑战,呵呵)或者他到你的网吧上(^_^,好像还不如人工销售啊?)。这样的话,你的客户限制在小区或者有限范围内。这些都是胡说,下面回答你的问题。
用JSP控制如你所说的硬件绝对没问题,我干过。通过JNI可以调用c++的DLL。而IC卡的接口有两种方式可以解决。第一,自己做一个IC卡读写器,自己定义传输数据协议。(可能至少需要两周时间,我当初从无到有,从学习到设计到实验成功花了1个月,你有经验另当别论,^_^),优点是技术全部自己拥有,缺点是要不断的测试检查有无BUG,同时如果没有大规模用户的话,你的模具费用相比就很大,成本就很高啦;第二就是买一个公司的读写设备,这样开发速的最快,通常两天就搞定,比如深圳的明华公司等等。后来我们卖软件的时候就是改成它了,呵呵,一天就否决了我们一个月做的东东。但是很多现成的各类DLL(各种语言的都有,还是专业分工好!)
回复
skyyoung 2001-10-10
http://java.sun.com/products/javacomm/index.html
回复
wanwangzhiwang 2001-10-10
哪位大侠知道?
回复
feiyangf 2001-10-10
回复
lci21 2001-10-10
JNI是Java本地接口,其允许Java代码操作由其他语言编写的应用程序和库。
回复
feiyangf 2001-10-10
Lesson: Integrating Java and Native Programs


The Java Native Interface defines a standard naming and calling convention so that the Java Virtual Machine (VM) can locate and invoke your native methods. This section shows you how to follow the JNI naming and calling conventions so that you can use JNI functions from a native method. It also teaches you how to declare types so that they can be correctly recognized by both the Java program and the native method.


Declaring Native Methods


On the Java side, you declare a native method with the native keyword and an empty method body. On the native side, you provide an implementation for the native method. You must be careful when writing native methods to "match" the native function implementation with the method signature in the Java header file. The javah tool, which is explained in Step 3: Create the .h file, helps you to generate native function prototypes that match the Java-side native method declaration.

Mapping between Java and Native Types


The JNI defines a mapping of Java types and native language (C/C++) types. This section introduces the native types corresponding to both primitive Java types, such as int and double, and Java objects, including strings and arrays.
回复
feiyangf 2001-10-10
具体做法如下:写一个*.java文件,编译成JNI FILE(jbuilder),他会自动生成*.h文件,然后再一个Bean中调用,最后再Jsp中调用。
下面是我们用的一个例子。

/**
* Title: <p>
* Description: <p>
* Copyright: Copyright (c) <p>
* Company: <p>
* @author
* @version 1.0
*/
package ScriptEditor;

public class ScriptTest {

public native String ProduceExcuteList(String s);
public native String Go(String s);
//public native String DelExcuteList();
static
{
System.loadLibrary("parserLib");
}
}

下面是一个教程,你看看,网上多得是。
Trail: Java Native Interface
Lesson: Interacting with Java from the Native Side

JNI Programming in C++


The JNI provides a slightly cleaner interface for C++ programmers. The jni.h file contains a set of inline C++ functions. This allows the native method programmer to simply write:


jclass cls = env->FindClass("java/lang/String");


instead of:


jclass cls = (*env)->FindClass(env, "java/lang/String");


The extra level of indirection on env and the env argument to FindClass is hidden from the programmer. The C++ compiler simply expands out the C++ member function calls to their C counterparts; therefore, the resulting code is exactly the same.

The jni.h file also defines a set of dummy C++ classes to enforce the subtyping relationships among different variations of jobject types:


class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
... /* more on jarray */
typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
... /* more on jarray */


The C++ compiler is therefore better able than the C compiler to detect when incorrect types are passed to methods. For example, it is incorrect to pass a jobject to GetMethodID because GetMethodID expects a jclass. You can see this by examining the GetMethodID signature:


jmethodID GetMethodID(jclass clazz, const char *name, const char *sig);


The C compiler treats jclass as the same as jobject because it makes this determination using the following typedef statement:


typedef jobject jclass;


Therefore a C compiler is not able to detect that you have mistakenly passed the method a jobject instead of jclass.

The added type safety in C++ comes with a small inconvenience. Recall from Accessing Java Arrays that in C you can fetch a Java string from an array of strings and directly assign the result to a jstring, as follows:


jstring jstr = (*env)->GetObjectArrayElement(env, arr, i);


In C++, however, you need to insert an explicit conversion of the Java string to jstring:


jstring jstr = (jstring)env->GetObjectArrayElement(arr, i);


You must make this explicit conversion because jstring is a subtype of jobject, which is the return type of GetObjectArrayElement.
回复
相关推荐
发帖
Web 开发
创建于2007-09-28

8.0w+

社区成员

Java Web 开发
申请成为版主
帖子事件
创建了帖子
2001-10-10 09:57
社区公告
暂无公告