[转载]Java网络加载协议(JNLP)及Java Web Start

2014年9月28日星期日 | | |

Java网络加载协议(JNLP)及Java Web Start
翻译:cherami
mail:[email protected]
 
原文:http://java.sun.com/jdc/JDCTechTips/2001/tt0530.html


在java开发的早期,重点被放在了客户端开发。语言中对于applet和安全下载的支持
对于万维网(WWW)的发布看上去是个不错的主意。但是现实是java最大的成功在于服务器端,java的强大功能和适应性赢得了服务器端开发者的心。同时,客户端的开发落后了。棘手的开发问题限制了applet的效用,开发者被迫转向基于浏览器的瘦客户端。

Java Network Launching Protocol (JNLP,java网络加载协议) 承诺改变这个现状。通过JCP(Java Community Process)的JSR-56的开发,
JNLP解决了很多先前用java开发针对客户端的功能的问题。一个JNLP客户端是一个应用程序或者说服务,它可以从宿主于网络的资源中加载应用程序。如果你使用JNLP打包一个应用程序,那么一个JNLP客户端能够:

o 为该应用探测,安装并且使用正确版本的JRE(java运行时环境)
o 从浏览器或者桌面加载应用程序
o 当新版本的应用出现时自动下载最新的版本。
o 为了加速启动速度在本机缓存应用程序需要的类
o 可以作为applet或者应用程序运行
o 在必要的情况下下载原始的库
o 以安全的方式使用诸如文件系统这样的本机资源
o 自动定位和加载外部依赖资源

Sun 提供了一个实现JNLP的称为Java Web Start(JWS)的参考实现。让我们使用它开发一个使用JFC Swing的简单应用。为了做这个,你需要从http://java.sun.com/products/javawebstart下载JWS。(译者注:JDK的新版本JDK1.4已经内置JWS,无须另外下载。)

下面是应用程序的代码:

//File HelloJNLP.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class HelloJNLP extends JFrame {
  public HelloJNLP() {
    super("Hello JNLP");
    String loadedFrom = this.getClass().getClassLoader().toString();
    JLabel jl = new JLabel("loaded by " + loadedFrom);
    JEditorPane jtp = new JEditorPane("text/plain",
                                                  "Edit this text");
    getContentPane().add(jl, BorderLayout.NORTH);
    getContentPane().add(jtp, BorderLayout.CENTER);
  }

  public static void main(String [] args) {
    JFrame f = new HelloJNLP();
    f.setBounds(100,100,325,250);
    f.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    f.setVisible(true);
    f.addWindowListener(new WindowAdapter() {
      public void windowClosed(WindowEvent e) {
        System.out.println("Shutting down...");
        System.exit(0);
      }
    });
  }
}

JNLP的核心是一个发布清单(deployment manifest)。它是一个使用.jnlp做
扩展名的XML文件(JNLP规范简单的称之为"JNLP 文件")。要发布HelloJNLP,你需要在JNLP文件中描述它,就像下面这样:

<?xml version="1.0" encoding="UTF-8"?>
<!-- file Hello.jnlp -->
<jnlp codebase="http://staff.develop.com/halloway/TechTips/May2001"
href="http://staff.develop.com/halloway/TechTips/May2001/Hello.jnlp">
        <information>
    <title>Hello</title>
    <vendor>Tech Tips Sample May 2001</vendor>
    <icon href="HelloJNLP.jpg"/>
  </information>
  <resources>
    <j2se version="1.2+"/>
    <jar href="HelloJNLP.jar"/>
  </resources>
  <application-desc main-class="HelloJNLP"/>
</jnlp>

这个清单包含客户端需要下载和使用HelloJNLP的所有信息:

o jnlp元素的codebase属性指出搜索应用程序资源的顶级URL。
o information元素指出一个JNLP用户接口可以显示给客户端的信息。
o j2se元素指出客户端必须有1.2版本或者更新的J2SE(tm)。(这是对于applet开发的一个大的提高,因为它常常受限于浏览器提供的VM(虚拟机))
o jar元素指出程序的JAR文件的相对于jnlp codebase位置。
o application-desc元素指出要运行的类。你可以添加子元素用以指定命令行参数或者系统属性。

要将这个应用程序发布给一个web服务器,你需要执行以下的步骤:

1. 修改jnlp的codebase和hrefURL为你自己的web服务器的合适的URL。
   
2. 将JNLP文件发布到web服务器。

3. 编译并打包HelloJNLP.java并发布到web服务器。例如:
  
   jar cvf HelloJNLP.jar HelloJNLP.class HelloJNLP$1.class
  
4. 创建一个图标HelloJNLP.jpg并将之安装在web服务器上。你可以使用
   http://staff.develop.com/halloway/TechTips/May2001/HelloJNLP.jpg
  
5. 设置你的web服务器的mime-type关联:.jnlp映射到mime-type application/x-java-jnlp-file。例如,对于Apache,添加如下一行到mime.types:
  
   application/x-java-jnlp-file    jnlp
  
   重新启动web服务器。

从客户端执行那个应用程序,首先确认你已经安装了JWS。然后简单的在浏览器中指向该jnlp文件。JWS客户端将下载该jnlp文件,下载必要的资源,加载应用程序。你所看到的将是在一个编辑区中显示的文本"Edit this text" 。如果你在配置web服务器上有问题或者不能使用web服务器,你可以从
http://staff.develop.com/halloway/TechTips/May2001/Hello.jnlp
加载这个程序。

注意HelloJNLP不是作为一个applet运行在浏览器中,而是作为一个独立的应用程序。

当你关闭程序时,HelloJNLP使用System.out打印消息"Shutting down...",然而没有控制台可见。控制台是JWS的诸多设置中缺省设置为"off"的其中一个。这是你可以修改的一对设置中的一个值,就像下面这样:

1. 编辑JWS安装目录中的javaws.cfg文件。添加一行"javaws.cfg.forceUpdate=true"。
这会导致JWS在启动应用程序前自动检查更新的版本。
  
2. 运行JWS。使用菜单File->Preferences,进入Advanced标签并且选择"Show Java Console"。(由于JDK1.4中的本机化,JWS将显示中文的界面,所以此处的因为被自动显示为对应的中文)同意,选择"Log Output"将日志输出到你选择的文件。当你在调试时并且需要捕获System.out和System.err时是非常有用的。

HelloJNLP显示一个编辑器,但是编辑器的内容在你关闭程序后将丢失。将下面的代码添加到HelloJNLP.java会自动的将编辑器的状态存储到客户端的硬盘上:

//changes to HelloJNLP.java
import java.io.*;
import java.net.*;
import javax.jnlp.*;

  //replace the constructor with this new version: 
  JEditorPane jtp;
  public HelloJNLP() {
    super("Hello JNLP, Second Version");
    String loadedFrom = this.getClass().getClassLoader().toString();
    JLabel jl = new JLabel("loaded by " + loadedFrom);
    jtp = new JEditorPane("text/plain", "Edit this text");
    readEditorContents();
    getContentPane().add(jl, BorderLayout.NORTH);
    getContentPane().add(jtp, BorderLayout.CENTER);

    addWindowListener(new WindowAdapter() {
        public void windowClosed(WindowEvent e) {
          System.out.println("Shutting down...");
          try {
            writeEditorContents();
          }
          catch (Exception ex) {
            System.out.println("Yoinks!");
            ex.printStackTrace();
          }
          System.exit(0);
        }
      });
  }

  //add these helper methods
  private void writeEditorContents() throws
                UnavailableServiceException, IOException {
      System.out.println("writeEditorContents");
    PersistenceService ps = (PersistenceService)
      ServiceManager.lookup("javax.jnlp.PersistenceService");
    BasicService bs = (BasicService)
      ServiceManager.lookup("javax.jnlp.BasicService");
    URL baseURL = bs.getCodeBase();
    System.out.println("CodeBase was " + baseURL);
    URL editorURL = new URL(baseURL, "Editor");
    try {
      ps.create(editorURL, 1024);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    FileContents fc = ps.get(editorURL);
    DataOutputStream os = new DataOutputStream(
                                 fc.getOutputStream(false));
    String s = jtp.getText();
    os.writeUTF(s);
    os.flush();
    os.close();
  }

  private void readEditorContents() {
    try {
      PersistenceService ps = (PersistenceService)
        ServiceManager.lookup("javax.jnlp.PersistenceService");
      BasicService bs = (BasicService)
        ServiceManager.lookup("javax.jnlp.BasicService");
      URL baseURL = bs.getCodeBase();
      URL editorURL = new URL(baseURL, "Editor");
      FileContents fc = ps.get(editorURL);
      DataInputStream is = new DataInputStream(fc.getInputStream());
      jtp.setText(is.readUTF());
      is.close();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

(译者注:正常编译需要在CLASSPATH中添加javaws.jar的路径,在windows下为C:/Program Files/Java Web Start目录下)
JNLP API定义了一套的服务用以绕过安全沙盒使得一些通常的客户端操作可以使用。
在writeEditorContents方法中,BasicService查找应用程序的代码目录,然后
PersistenceService将编辑区的内容缓存在本机硬盘上,这些内容被键入到一个和应用程序目录相对的URL下。PersistenceService提供的名字/值对数据和浏览器的cookies很相似。JWS通过一对被称为"muffins"的东西实现了这个,muffins不时候用来存储大数据,他们应该用于在客户端缓存小的标识符。然后这些标识符能被用于在服务器上定位大的信息。

在web服务器上重新发布修改过的应用程序,然后试着从客户端运行它--依然通过URL。如果你没有web服务器,你可以从
http://staff.develop.com/halloway/TechTips/May2001/Hello2.jnlp 运行这个新版本。JWS自动探测程序被改变并运行新的版本。你可以通过检查标题条的字符串来证实这点,它现在将显示"Hello JNLP,Second Version." 修改编辑区的内容,然后关闭它。当你再次加载该程序时,你的改变会出现。(当你第一次运行新版的程序时你会在控制台看到一个异常,这是因为readEditorContents 第一次不能找到muffin。)
(译者注:实际上第一次运行时出现的异常导致程序无法正常结束,从而使得编辑区的内容无法写入客户端,下次运行时也相当于第一次运行。即此程序无法展示文章的特性,可能是于笔者的JWS的版本有关,笔者使用的是最新的1.0.1_02(build b03))
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/

 

0 评论:


所有文章收集于网络,如果有牵扯到版权问题请与本站站长联系。谢谢合作![email protected]