Java Network Launching Protocol
在java开发的早期,重点被放在了客户端开发。语言中对于applet和安全下载的支持
对于万维网(WWW)的发布看上去是个不错的主意。但是现实是java最大的成功在于服务器端,java的强大功能和适应性赢得了服务器端开发者的心。同时,客户端的开发落后了。棘手的开发问题限制了applet的效用,开发者被迫转向基于浏览器的瘦客户端。
JavaNetworkLaunchingProtocol(JNLP,java网络加载协议)承诺改变这个现状。通过JCP(JavaCommunityProcess)的JSR-56的开发,
JNLP解决了很多先前用java开发针对客户端的功能的问题。一个JNLP客户端是一个应用程序或者说服务,它可以从宿主于网络的资源中加载应用程序。如果你使用JNLP打包一个应用程序,那么一个JNLP客户端能够:
o为该应用探测,安装并且使用正确版本的JRE(java运行时环境)
o从浏览器或者桌面加载应用程序
o当新版本的应用出现时自动下载最新的版本。
o为了加速启动速度在本机缓存应用程序需要的类
o可以作为applet或者应用程序运行
o在必要的情况下下载原始的库
o以安全的方式使用诸如文件系统这样的本机资源
o自动定位和加载外部依赖资源
Sun提供了一个实现JNLP的称为JavaWebStart(JWS)的参考实现。让我们使用它开发一个使用JFCSwing的简单应用。为了做这个,你需要从http://java.sun.com/products/javawebstart下载JWS。(译者注:JDK的新版本JDK1.4已经内置JWS,无须另外下载。)
下面是应用程序的代码:
//FileHelloJNLP.java
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
publicclassHelloJNLPextendsJFrame{
publicHelloJNLP(){
super("HelloJNLP");
StringloadedFrom=this.getClass().getClassLoader().toString();
JLabeljl=newJLabel("loadedby"+loadedFrom);
JEditorPanejtp=newJEditorPane("text/plain",
"Editthistext");
getContentPane().add(jl,BorderLayout.NORTH);
getContentPane().add(jtp,BorderLayout.CENTER);
}
publicstaticvoidmain(String[]args){
JFramef=newHelloJNLP();
f.setBounds(100,100,325,250);
f.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
f.setVisible(true);
f.addWindowListener(newWindowAdapter(){
publicvoidwindowClosed(WindowEvente){
System.out.println("Shuttingdown...");
System.exit(0);
}
});
}
}
JNLP的核心是一个发布清单(deploymentmanifest)。它是一个使用.jnlp做
扩展名的XML文件(JNLP规范简单的称之为"JNLP 文件")。要发布HelloJNLP,你需要在JNLP文件中描述它,就像下面这样:<!--l version="1.0" encoding="UTF-8-->
<!-- file Hello.jnlp -->
href="http://staff.develop.com/halloway/TechTips/May2001/Hello.jnlp">
TechTipsSampleMay2001
这个清单包含客户端需要下载和使用HelloJNLP的所有信息:
ojnlp元素的codebase属性指出搜索应用程序资源的顶级URL。
oinformation元素指出一个JNLP用户接口可以显示给客户端的信息。
oj2se元素指出客户端必须有1.2版本或者更新的J2SE(tm)。(这是对于applet开发的一个大的提高,因为它常常受限于浏览器提供的VM(虚拟机))
ojar元素指出程序的JAR文件的相对于jnlpcodebase位置。
oapplication-desc元素指出要运行的类。你可以添加子元素用以指定命令行参数或者系统属性。
要将这个应用程序发布给一个web服务器,你需要执行以下的步骤:
1.修改jnlp的codebase和hrefURL为你自己的web服务器的合适的URL。
2.将JNLP文件发布到web服务器。
3.编译并打包HelloJNLP.java并发布到web服务器。例如:
jarcvfHelloJNLP.jarHelloJNLP.classHelloJNLP$1.class
4.创建一个图标HelloJNLP.jpg并将之安装在web服务器上。你可以使用
http://staff.develop.com/halloway/TechTips/May2001/HelloJNLP.jpg
5.设置你的web服务器的mime-type关联:.jnlp映射到mime-typeapplication/x-java-jnlp-file。例如,对于Apache,添加如下一行到mime.types:
application/x-java-jnlp-filejnlp
重新启动web服务器。
从客户端执行那个应用程序,首先确认你已经安装了JWS。然后简单的在浏览器中指向该jnlp文件。JWS客户端将下载该jnlp文件,下载必要的资源,加载应用程序。你所看到的将是在一个编辑区中显示的文本"Editthistext"。如果你在配置web服务器上有问题或者不能使用web服务器,你可以从
http://staff.develop.com/halloway/TechTips/May2001/Hello.jnlp
加载这个程序。
注意HelloJNLP不是作为一个applet运行在浏览器中,而是作为一个独立的应用程序。
当你关闭程序时,HelloJNLP使用System.out打印消息"Shuttingdown...",然而没有控制台可见。控制台是JWS的诸多设置中缺省设置为"off"的其中一个。这是你可以修改的一对设置中的一个值,就像下面这样:
1.编辑JWS安装目录中的javaws.cfg文件。添加一行"javaws.cfg.forceUpdate=true"。
这会导致JWS在启动应用程序前自动检查更新的版本。
2.运行JWS。使用菜单File->Preferences,进入Advanced标签并且选择"ShowJavaConsole"。(由于JDK1.4中的本机化,JWS将显示中文的界面,所以此处的因为被自动显示为对应的中文)同意,选择"LogOutput"将日志输出到你选择的文件。当你在调试时并且需要捕获System.out和System.err时是非常有用的。
HelloJNLP显示一个编辑器,但是编辑器的内容在你关闭程序后将丢失。将下面的代码添加到HelloJNLP.java会自动的将编辑器的状态存储到客户端的硬盘上:
//changestoHelloJNLP.java
importjava.io.*;
importjava.net.*;
importjavax.jnlp.*;
//replacetheconstructorwiththisnewversion:
JEditorPanejtp;
publicHelloJNLP(){
super("HelloJNLP,SecondVersion");
StringloadedFrom=this.getClass().getClassLoader().toString();
JLabeljl=newJLabel("loadedby"+loadedFrom);
jtp=newJEditorPane("text/plain","Editthistext");
readEditorContents();
getContentPane().add(jl,BorderLayout.NORTH);
getContentPane().add(jtp,BorderLayout.CENTER);
addWindowListener(newWindowAdapter(){
publicvoidwindowClosed(WindowEvente){
System.out.println("Shuttingdown...");
try{
writeEditorContents();
}
catch(Exceptionex){
System.out.println("Yoinks!");
ex.printStackTrace();
}
System.exit(0);
}
});
}
//addthesehelpermethods
privatevoidwriteEditorContents()throws
UnavailableServiceException,IOException{
System.out.println("writeEditorContents");
PersistenceServiceps=(PersistenceService)
ServiceManager.lookup("javax.jnlp.PersistenceService");
BasicServicebs=(BasicService)
ServiceManager.lookup("javax.jnlp.BasicService");
URLbaseURL=bs.getCodeBase();
System.out.println("CodeBasewas"+baseURL);
URLeditorURL=newURL(baseURL,"Editor");
try{
ps.create(editorURL,1024);
}
catch(Exceptione){
e.printStackTrace();
}
FileContentsfc=ps.get(editorURL);
DataOutputStreamos=newDataOutputStream(
fc.getOutputStream(false));
Strings=jtp.getText();
os.writeUTF(s);
os.flush();
os.close();
}
privatevoidreadEditorContents(){
try{
PersistenceServiceps=(PersistenceService)
ServiceManager.lookup("javax.jnlp.PersistenceService");
BasicServicebs=(BasicService)
ServiceManager.lookup("javax.jnlp.BasicService");
URLbaseURL=bs.getCodeBase();
URLeditorURL=newURL(baseURL,"Editor");
FileContentsfc=ps.get(editorURL);
DataInputStreamis=newDataInputStream(fc.getInputStream());
jtp.setText(is.readUTF());
is.close();
}
catch(Exceptione){
e.printStackTrace();
}
}
(译者注:正常编译需要在CLASSPATH中添加javaws.jar的路径,在windows下为C:\ProgramFiles\JavaWebStart目录下)
JNLPAPI定义了一套的服务用以绕过安全沙盒使得一些通常的客户端操作可以使用。
在writeEditorContents方法中,BasicService查找应用程序的代码目录,然后
PersistenceService将编辑区的内容缓存在本机硬盘上,这些内容被键入到一个和应用程序目录相对的URL下。PersistenceService提供的名字/值对数据和浏览器的cookies很相似。JWS通过一对被称为"muffins"的东西实现了这个,muffins不时候用来存储大数据,他们应该用于在客户端缓存小的标识符。然后这些标识符能被用于在服务器上定位大的信息。
在web服务器上重新发布修改过的应用程序,然后试着从客户端运行它--依然通过URL。如果你没有web服务器,你可以从
http://staff.develop.com/halloway/TechTips/May2001/Hello2.jnlp运行这个新版本。JWS自动探测程序被改变并运行新的版本。你可以通过检查标题条的字符串来证实这点,它现在将显示"HelloJNLP,SecondVersion."修改编辑区的内容,然后关闭它。当你再次加载该程序时,你的改变会出现。(当你第一次运行新版的程序时你会在控制台看到一个异常,这是因为readEditorContents第一次不能找到muffin。)
(译者注:实际上第一次运行时出现的异常导致程序无法正常结束,从而使得编辑区的内容无法写入客户端,下次运行时也相当于第一次运行。即此程序无法展示文章的特性,可能是于笔者的JWS的版本有关,笔者使用的是最新的1.0.1_02(buildb03))
JNLP提供了比这里演示的更多的服务。例如,你可以:
o很好的控制程序如何被下载
o描述各个JAR之间的依赖关系
o下载并运行本机代码安装程序
o对签名的代码授予附件的权限
o请求指定版本的程序或者applet
要了解更多有关JNLP的情况,请到
http://java.sun.com/products/javawebstart/download-spec.html下载JNLP规范。
要了解更多JWS的情况,请参考http://java.sun.com/products/javawebstart/