在JavaScript程序中整合Java函数
问题的提出:
假定我们的HTML 页中有一些表单需要处理,并且我们需要初始化数据库中的字段,我们该怎么办?标准的解决办法就是使用CGI脚本或是使用Java Servlet等服务器端程序,但是你有没有想过,还可以编写一个脚本程序使你可以用JavaScript直接调用服务器端Java程序进行任何计算的结果,就像下面代码中列的那样:
<html>
我们的表单<br>
<form>
<input type="text" name="textField"><br>
<input type="button" value="Click" onClick="updateField();">
</form>
<script>
function updateField()
{
document.forms[0].textField.value=java.dbConnection.getData('SQL_expr');
}
</script>
</html>
所以,当用户点击按钮,该程序就会调用Java的dbConnection类,然后就可以在JavaScript应用程序中使用Java返回的结果了。上面的程序只是更新了textField的值,我们不必刷新整个页面。由于这个程序也不是JSP文件,所以不必把你的页面编译成Java Servlet。
你还可以使用调用某些Java函数返回的结果替代段落中的文字;在一个非常大的HTML表中,你可以使用同样的方法,只要更新一行信息:
<script language="JavaScript">
function changeCol()
{
document.all.quote.rows[0].cells[1].innerText=java.Stock.getQuote('Wayne');
}
</script>
<table id="quote" border=1>
<tr><td>Wayne</td><td>123</td></tr>
<tr><td>Another one</td><td>34</td></tr>
</table>
怎么样,能够直接在JavaScript中嵌入Java对象的调用,而且可以保证你的页面的其它部分不会被改变,你一定对这个问题比较感兴趣吧。好吧,现在就让我们用Java Servlet来实现它吧。
工作原理
我的构思是使用Java Servlet编写一个JinJ(Java in JavaScript)程序,能够让 HTML 页面内整合 Java 类和 JavaScript脚本,将所有使用JavaScript调用Java函数的HTML页面都传送到这个Servlet中处理,JinJ将即时产生带有Java applet的隐藏帧,这个applet起着桥梁的作用,用来和Java通讯,它的大小还不到2KB,为了调用对象,Applet使用HTTP连接同一个JinJ Servlet。JinJ用相应的JavaScript结构来替换所有的Java调用(其实每一个调用前都有前缀java),并且保持页面的其它部分不变。所以,为了能够让JinJ正确识别,你的JavaScript代码中的任何Java调用,都要使用如下的结构: java.object_name.function_name(list_of_parameters) 其中:
java:是一个标准的前缀:
object_name:是一个变量名,保存某些Java类的实例(在后面,我们会详细的探讨如何创建/定义这样的变量),比如说它可以是一个类名。
function_name:是一个Java类的函数名。
list_of_parameters:是调用的函数的参数列表(可以为空)。
下面也将会探讨如何把参数传给Java,每个参数显然是某个JavaScript表达式。你也可以使用预编译页,换句话说就是在使用HTML页面之前编译它。
服务器实现
在谈编程之前我还想说一说JinJ的服务器实现。在你的服务器中需要两个组成部分:JINJ servlet和bridge applet.。你所有需要被整合的HTML页面都是被JinJ servlet预处理,你可以通过以下方法实现:
a)把你的页面当作一个参数传递给JinJ servlet
所以你可以把你HTML页中如下的超链接<a href="mypage.html">我的页面</a>改为:<a href="
http://myhost/servlet/JinJ">
http://myhost/servlet/JinJ?mypage.html"">
http://myhost/servlet/JinJ">
http://myhost/servlet/JinJ?mypage.html" target="_top">我的页面</a>
JinJ servlet 随即把所有的Java调用转化成JavaScript,然后产生页面并输出出来。
b)定义某种映射。举例来说,这里我们设置Servlet调用扩展名为.JinJ的文件 (这里介绍的方法针对运行于NT上的JSWDK,你所使用的Servelt系统的具体定义映射的方法请参看你的用户指南)
JinJ.code=JinJ
JinJ.initparams=servlet=
http://myhost/servlet/JinJ">
http://myhost/servlet/JinJ, root=c:\\jswdk\\webpages objects=c:\\jswdk\\obj.txt,
codebase=
http://myhost/
设置了以后,你就可以使用
http://myhost/some_file.JinJ然后JinJ servlet将会自动被调用了
Servlet参数
为了让JinJ正常运行,还应当能够为JinJ servlet设置一些全局参数,这也是我们在编程是应当注意到的问题。对于JSWDK, Servlet属性是参数'initparams'的一部分;对于Tomcat,则应当编辑系统中的web.xml
1)需要设置JinJ servlet的路径,参数名是servlet,就像这样:
http://your_host:port/servlet/JINJ
2) 需要设置存放HTML页的根目录,参数名是root,其值就像这样: /home/webpages (或c:\webpages) (默认值是一个空的字符串)。 例如,如果你的根目录是:/home/webpages,并且你使用
http://host/servlet/JINJ?myfile.htm 的话,那你的真实的文件名是/home/webpages/myfile.htm
3) 需要设置你定义的Java对象的文件的完全目录,参数名是objects,其值只是一个存放在你的服务器中的一个文件的文件名。
4) 需要设置用于java applet的codebase,参数名是codebase,定义了存放applet的类的路径,比如
http://your_host/">
http://your_host/ 。
以下为Tomcat中web.xml 中的相应设置:
<servlet>
<servlet-name>JinJ</servlet-name>
<servlet-class>JinJ</servlet-class>
<init-param>
<param-name>servlet</param-name>
<param-value>
http://your_host/">
http://your_host/servlet/JinJ</param-value>
</init-param>
<init-param>
<param-name>root</param-name>
<param-value>path_to_the_root_directory</param-value>
</init-param>
<init-param>
<param-name>objects</param-name>
<param-value>path_to_your_objects_file</param-value>
</init-param>
<init-param>
<param-name>codebaset</param-name>
<param-value>
http://your_host/">
http://your_host/</param-value>
</init-param>
</servlet>
在编程时还应当注意实现读取对象文件中所包含的对象,或创建新的Java对象
1. 在初始化JinJ Servlet时将创建一些Java对象,因此你必须在某个文本文件中定义这些对象,并且在参数objects中设置这个文件的路径,这是一个文本文件,每一行描述了一个元素(空行和以#或//开头的行将被忽略),使用下面的格式来描述参数:
object_name=new class_name(list_of_parameters);
换句话说,它很象每次调用类构造器的new操作符,object_name 是就是某个你将在JavaScript中使用到的标识符,例如:
file://数据池
A=new dbPool(5);
file://哈希表
B=new java.util.Hashtable();
或者使用你自己定义的包和类:c=new myPackage.myClass(true);
然后在JavaScript中,你就可以使用它们了,如A.connect(),其中connect() 是类dbPool的一个函数,或者使用B.put('key',value)
注意:
1)你的类应当放在你的CLASSPATH指定的目录下面。
2)你可以使用类名作为对象名 (不过这必须在没有重名的情况下,因为对象名必须是唯一的),如:dbPool=new dbPool();
2. 为了更优化程序,应当在编程的时候就预建一些对象组成标准化库,通过标准化库,你可以在运行中创建/删除对象的实例,查看存在的对象等。
下面就是我们需要实现的标准化库(预定义的对象) ,对象名是JinJ,所以服务器端名为JinJ的实例将会被自动创建,这给类中有下面几个函数:
public boolean create(String object_name,String class_name,list_of_constructors_parameters);
public void delete(String object_name);
public boolean exists(String object_name);
public String id();
public int random(int n1, int n2);
public String rewriteURL(document,String id_name);
public String rewriteURL(document,String id_name,String id_value);
create(创建):能够让你在运行时创建某个对象的实例:
参数为:
- 新的对象名
- 类名
- 参数列表 (如果你使用默认构造函数的时候,可以为空)。
返回值:
- 根据创建的接过返回true 或 false
例如:
<script language="JavaScript">
<!--创建myHash 作为一个新的哈希表Hashtable -->
java.JinJ.create("myHash","java.util.Hashtable");
<!-- 使用myHash -->
java.myHash.put('key1','shop1');
</script>
delete:让你删除任何对象的实例。
参数为:
- 对象名
例如:
<script language="JavaScript">
<!-- 删除myHash -->
java.JinJ.delete("myHash");
</script>
exists:查看对象的实例是否存在。
参数为:
- 对象名
返回值:
true 或 false
例如:
<script language="JavaScript">
if (java.JinJ.exists("myHash")=='false')
{
<!--创建myHash 作为一个新的哈希表Hashtable -->
java.JinJ.create(myHash,"java.util.Hashtable");
<!--使用myHash -->
java.myHash.put('key1','shop1');
</script>
id: 返回唯一的标示符,比如你可以在支持session的程序中使用这个函数。
R
private String prepareString(String s)
{
if(s == null)
return null;
if(s.length() < 2)
return s;
if(s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"') file://如果字符串s以"开头,又以"结尾,则把两头的"去掉
return s.substring(1, s.length() - 1);
if(s.startsWith("'") && s.endsWith("'"))//如果字符串s以'开头,又以'结尾,则把两头的"去掉
return s.substring(1, s.length() - 1);
else
return s;
}
private boolean processLine1(String s, String s1, String s2, String s3)
{ file://创建一个Object类的实例
Object obj[] = {
null, null
};
Class class1;
if((class1 = (Class)cls.get(s1)) == null)
try
{
class1 = Class.forName(s1);//返回具有s1指定的类名的类的Class描述符
cls.put(s1, class1); file://放回哈西表
}
catch(Exception _ex)
{
System.out.println("不能装载类 " + s1);
return false;
}
obj[0] = class1;
if(s2.length() == 0)
{
try
{
obj[1] = class1.newInstance();// 创建类的新实例
}
catch(NoSuchMethodError _ex)
{
System.out.println("不能创建对象的实例 " + s3);
return false;
}
catch(Exception _ex)
{
System.out.println("不能创建对象的实例 " + s3);
return false;
}
inst.put(s, ((Object) (obj)));
}
else
{
Object obj1[] = getParamArray(s2, 0); file://取得参数列表
if(obj1 == null)
{
System.out.println("不能处理这行参数: " + s3);
return false;
}
try
{
Constructor cons = class1.getConstructor((Class[])obj1[0]);//使用参数列表构建类构建器
if(class1 == null)
{
System.out.println("不能创建对象的实例 " + s3);
return false;
}
obj[1] = cons.newInstance((Object[])obj1[1]);
inst.put(s, ((Object) (obj)));
}
catch(Exception _ex)
{
System.out.println("不能创建对象的实例 " + s3);
return false;
}
}
return true;
}
private Object[] getParamArray(String s, int i) file://取得s中所包含的参数列表,存入一个Object数组中
{
String s1 = s.trim(); file://去除末尾的空格
String s6 = ",";
Vector vector = new Vector();
if(s1.length() > 0)
{
if(s1.charAt(0) == '(')//去掉(
s1 = s1.substring(1);
if(s1.endsWith(")"))//去掉)
s1 = s1.substring(0, s1.length() - 1);
String s2 = s1.trim(); file://去掉字符串后的空格
if(s2.length() == 0 && i == 0) file://如果s2长度为零,说明没有参数
return null;
String s4;
if(s2.length() > 0)
{
if(s2.charAt(0) == '"')//如果s2以"开头,则用\"代替
s6 = "\"";
else
if(s2.charAt(0) == '\'')//如果s2以\'开头,则用'代替
s6 = "'";
else
s6 = ",";
s4 = s2.substring(0, 1); file://取得s2的第一个字符
s2 = s2.substring(1);//取得s2的第二个字符往后的字符
}
else
{
s4 = "";
}
for(; s2.length() > 0; s2 = s2.substring(1))//这些语句是为了把参数表中的参数分离出来
{
String s5 = s2.substring(0, 1);//取得s2的第一个字符
if(s5.equals(s6)) file://如果s5和s6相等,即判断s5是否为两个参数的分隔符
{
if(s6.equals("\"") || s6.equals("'"))//如果s6为\"或',则
s4 = s4 + s6;
vector.addElement(s4); file://把s4(即一个参数值)加入vector中
s2 = s2.substring(1).trim();
s4 = "";
if(s2.length() == 0)
break;
s4 = s2.substring(0, 1);
if(s4.charAt(0) == ',')
file://如果s4以","开头,则判断s4是否以分隔符开头
{
s2 = s2.substring(1).trim();
s4 = "";
if(s2.length() == 0)
break;
s4 = s2.substring(0, 1);
}
if(s4.charAt(0) == '"')
s6 = "\"";
else
if(s4.charAt(0) == '\'')
s6 = "'";
else
s6 = ",";
}
else
{
s4 = s4 + s5;
}
}
if(s4.length() > 0)
vector.addElement(s4);
}
int j = vector.size(); file://取得向量变量vector的大小,vector中保存的都是参数值
if(j == 0 && i == 0)
return null; file://没有参数
Object aobj[] = new Object[3];
Class class1[] = new Class[j + i];
Object aobj1[] = new Object[j + i];
aobj[0] = class1;
aobj[1] = ((Object) (aobj1));
aobj[2] = new Integer(j + i);
for(int k = i; k < j + i; k++)
{
String s3 = (String)vector.elementAt(k - i);
try
file://以下的代码是为了判断,每个参数到底是什么数据类型
{ file://判断是否为整型
Integer integer = Integer.valueOf(s3);
class1[k] = Integer.TYPE;
aobj1[k] = integer;
}
catch(Exception _ex)
{
try
{ file://判断是否为浮点型
Float float1 = Float.valueOf(s3);
class1[k] = Float.TYPE;
aobj1[k] = float1;
}
catch(Exception _ex2)
{ file://判断是否为布尔类型
s3 = prepareString(s3);
if(s3.equals("true")) file://判断是否为真
{
class1[k] = Boolean.TYPE;
aobj1[k] = new Boolean(true);
}
else
if(s3.equals("false")) file://判断是否为假
{
class1[k] = Boolean.TYPE;
aobj1[k] = new Boolean(false);
}
else
{
class1[k] = class$java$lang$String == null ? (class$java$lang$String = class$("java.lang.String")) : class$java$lang$String;
aobj1[k] = s3;
}
}
}
}
vector = null;
return aobj;
}
public void doPost(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse)
throws ServletException, IOException
{
doGet(httpservletrequest, httpservletresponse);
}
public void doGet(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse)
throws ServletException, IOException
{
String s = "";
String s1 = "";
s = HttpUtils.getRequestURL(httpservletrequest).toString();
file://返回一个字符串,包含请求字符串中包含的协议名,主机名,端口号以及路径,但是不包含请求的内容
int i;
if((i = s.indexOf("?")) > 0)//去掉s结尾的问号
s = s.substring(0, i);
s1 = httpservletrequest.getQueryString();//取的请求字符串的请求内容
if(s1 == null)
{
parseFile(s1, httpservletrequest, httpservletresponse);
return;
}
if(s1.equals(admin)) file://如果请求串等于admin
{
adminPage(httpservletrequest, httpservletresponse, s); file://进入管理员界面
return;
}
if(s1.equals("namiotde")) file://如果请求串为namiotde,则执行相应的执行命令
{
executeRequest(httpservletrequest, httpservletresponse);
return;
}
String s2 = getFromQuery(s1, "what="); file://取的动作的属性值
if(s2.equals("hframe"))
{
getHiddenFrame(httpservletrequest, httpservletresponse);//转到隐藏帧
return;
}
if(s2.equals("mframe"))
{
getMainFrame(s1, httpservletrequest, httpservletresponse); file://转到主帧
return;
}
else
{
parseFile(s1, httpservletrequest, httpservletresponse);
return;
}
}
file://管理员界面,执行添加删除对象的操作
private void adminPage(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse, String s)
throws IOException
{
PrintWriter printwriter = httpservletresponse.getWriter();
String s1 = "";
String s4 = httpservletrequest.getParameter("what"); file://返回请求串中what,即动作的属性值
if(s4 != null)
if(s4.equals("delete")) file://如果what的值为delete,则执行相应的操作
{
for(Enumeration enumeration = httpservletr