摘 要
波音产品结构管理树是一个基于RMI,SWING和Java web start等多种技术的一个窗口应用程序,现已投入使用。
论文第一章对波音产品结构管理树的历史开发过程,开发碰到的难题,和怎样解决这些难题进行了阐述,并且对波音产品结构管理树进行了简单的介绍。
第二章介绍了开发所采用的RMI技术(远程方法调用技术),对它的优点,架构进行了简单的介绍。
第三章介绍了开发所采用的另一个关键技术SWING。
第四章介绍了软件部署所采用的技术JAVA WEB START.
第五章是程序设计分析阶段,给出了需求分析图,
第六章是程序的具体实现过程
关键词:RMI,Swing,Java Web Start,GUI,产品数据管理
一、 前言
1. 问题的提出
波音文档与生产控制管理系统采用的是JSP+Tomcat+SQL Server的浏览器-服务器(BS)形式,起初,曾尝试完全采用Web页实现产品结构树的显示、查询与编辑功能,并成功改造了一个采用WEB+JavaScript+XML的代码包,基本达到了功能要求。但随着使用的深化,暴露出一些很难解决的问题:
1) 由于JavaScript完全在客户端运行,对结构树的编辑结果不能实时反映在服务器上,需要在编辑完成后提交才能生效,这样容易造成数据不一致,不利于数据的同步更新;
2) 所有结构树上各节点的信息均保存在XML文件中,并未将其转存在数据库中,造成按条件查询有一定困难。
鉴于上述情况,我们后来抛弃了这种做法,改用Java中的远程方法调用技术,很好地解决了这些问题。我们分别开发了客户端和服务器端的应用程序,客户端负责界面显示与用户操作,服务器端负责数据存取,服务器端的类向客户端暴露若干方法供其调用,这样,每当客户端对结构树进行编辑时,客户端马上调用服务器端相关方法,将编辑好的数据实时存放在数据库中。由于所有数据均存放在数据库中,此时可充分利用数据库的检索技术按检索条件进行快速的查询
2.波音产品结构管理树简介
产品结构管理树是一个基于Java Remote Method Invocation ( RMI -- Java远程方法调用),Swing(Java 图形化用户界面GUI),Java Web Start,SQL Server2000等流行先进技术的图形化应用程序.它主要有以下特点:
1) 采用SQL Server2000作为数据库,解决了以往XML技术不便查询,和数据不能实时更新的弊病。
2) 使用Swing技术的图形化用户界面,不但使界面美观,而且应用程序能够支持更多的操作(几乎能想象的就能实现),这就克服了单一WEB技术很多功能受限的弱点
3) RMI远程方法技术的使用,这种分布式计算技术具有安全,便于编写和使用,编写一次,到处使用等优点,并且特别适合在有大量数据库操作的程序中使用。
4) 由于程序是一个GUI的应用程序,如何使它在网页中运行是一个问题,Sun公司提供的Java Web Start技术完美的解决了这个难题。
二、 RMI远程方法调用技术的介绍
1.概述
Java Remote Method Invocation ( RMI -- Java远程方法调用)允许您使用Java编写分布式对象。本文将介绍RMI的优点以及 如何将其连接到现有的和原有的系统中,以及与用Java 编写的组件的连接。
RMI为采用Java对象的分布式计算提供了简单而直接的 途径。这些对象可以是新的Java对象,也可以是围绕现有API的简单的Java包装程序。Java体现了“编写一次就 能在任何地方运行的模式。而RMI可将Java模式进行扩展 ,使之可在任何地方运行”。
因为RMI是以Java为核心的,所以,它将Java的安全性 和可移植性等强大功能带给了分布式计算。您可将代理和梢?务逻辑等属性移动到网络中最合适的地方。如果您 要扩展Java在系统中的使用,RMI将使您充分利用其强大 功能。
RMI可利用标准Java本机方法接口JNI与现有的和原有的 系统相连接。RMI还可利用标准JDBC包与现有的关系数据库连接。RMI/JNI和RMI/JDBC相结合,可帮助您利用RMI与目 前使用非Java语言的现有服务器进行通信,而且在您需 要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。
2.优点
从最基本的角度看,RMI是Java的远程过程调用(RPC)机 制。与传统的RPC系统相比,RMI具有若干优点,因为它是Java面向对象方法的一部分。传统的RPC系统采用中性 语言,所以是最普通的系统--它们不能提供所有可能的 目标平台所具有的功能。
RMI以Java为核心,可与采用本机方法与现有系统相连 接。这就是说,RMI可采用自然、直接和功能全面的方式为您提供分布式计算技术,而这种技术可帮助您以不断 递增和无缝的方式为整个系统添加Java功能。
RMI的主要优点如下:
1)面向对象:RMI可将完整的对象作为参数和返回值进行传 递,而不仅仅是预定义的数据类型。也就是说,您可以将类似Java哈希表这样的复杂类型作为一个参数进行传 递。而在目前的RPC系统中,您只能依靠客户机将此类对 象分解成基本数据类型,然后传递这些数据类型,最后 在服务器端重新创建哈希表。RMI则不需额外的客户程序代码(将对象分解成基本数据类型),直接跨网传递对象 。
2)可移动属性:RMI可将属性(类实现程序)从客户机移动 到服务器,或者从服务器移到客户机。例如,您可以定义一个检查雇员开支报告的接口,以便察看雇员是否遵 守了公司目前实行的政策。在开支报告创建后,客户机 就会从服务器端获得实现该接口的对象。如果政策发生 变化,服务器端就会开始返回使用了新政策的该接口的另一个实现程序。您不必在用户系统上安装任何新的软 件就能在客户端检查限制条件--从而向用户提供烁?快的 反馈,并降低服务器的工作量。这样就能具备最大的灵 活性,因为政策改变时只需要您编写一个新的Java类,并将其在服务器主机上安装一次即可。
3)设计方式:对象传递功能使您可以在分布式计算中充分 利用面向对象技术的强大功能,如二层和三层结构系统。如果您能够传递属性,那么您就可以在您的解决方案 中使用面向对象的设计方式。所有面向对象的设计方式 无不依靠不同的属性来发挥功能,如果不能传递完整的 对象--包括实现和类型--就会失去设计方式上所提供的优点。
4)安全:RMI使用Java内置的安全机制保证下载执行程序时用户系统的安全。RMI使用专门为保护系统免遭恶意小应 用程序侵害而设计的安全管理程序,可保护您的系统和 网络免遭潜在的恶意下载程序的破坏。在情况严重时, 服务器可拒绝下载任何执行程序。
5)便于编写和使用:RMI使得Java远程服务程序和访问这些 服务程序的Java客户程序的编写工作变得轻松、简单。远程接口实际上就是Java接口。服务程序大约用三行指 令宣布本身是服务程序,其它方面则与任何其它Java对 象类似。这种简单方法便于快速编写完整的分布式对象 系统的服务程序,并快速地制做软件的原型和早期版本,以便于进行测试和评估。因为RMI程序编写简单,所以 维护也简单。
6)可连接现有/原有的系统:RMI可通过Java的本机方法接 口JNI与现有系统进行进行交互。利用RMI和JNI,您就能用Java语言编写客户端程序,还能使用现有的服务器端 程序。在使用RMI/JNI与现有服务器连接时,您可以有选择 地用Java重新编写服务程序的任何部分,并使新的程序充分发挥Java的功能。类似地,RMI可利用JDBC、在不修 改使用数据库的现有非Java源代码的前提下与现有关系 数据库进行交互。
7)编写一次,到处运行:RMI是Java“编写一次,到处运行 ”方法的一部分。任何基于RMI的系统均可100%地移植到任何Java虚拟机上,RMI/JDBC系统也不例外。如果使用RMI/JNI 与现有系统进行交互工作,则采用JNI编写的代码可与任 何Java虚拟机进行编译、运行。
8)分布式垃圾收集:RMI采用其分布式垃圾收集功能收集不 再被网络中任何客户程序所引用的远程服务对象。与Java 虚拟机内部的垃圾收集类似,分布式垃圾收集功能允许 用户根据自己的需要定义服务器对象,并且明确这些对 象在不再被客户机引用时会被删除。
9)并行计算:RMI采用多线程处理方法,可使您的服务器利 用这些Java线程更好地并行处理客户端的请求。
Java分布式计算解决方案:RMI从JDK 1.1开始就是Java平台 的核心部分,因此,它存在于任何一台1.1 Java虚拟机中 。所有RMI系统均采用相同的公开协议,所以,所有Java 系统均可直接相互对话,而不必事先对协议进行转换。
3.体系结构
RMI系统可为分布式面向对象计算提供简单而又直接的 基础。其体系结构可允许对服务器和引用类型进行扩展,从而使RMI能以连续的方式添加功能。
当服务程序被输出后,其引用类型就被定义。在上面 的例子中,我们将服务器作为UnicastRemoteObject服务器输出,即点到点非复制服务器。对这些对象的引用对于这 类服务器非常合适。不同类型的服务器有不同的引用句 法。例如,MulticastRemoteObject就有允许复制服务的引用句法。
当客户机收到向服务器的引用后,RMI就会下载一个可 将该引用上的调用转换为面向服务器的远程调用的存根。
存根使用对象序列化法将参数编组到方 法中,并通过网络将经过编组的调用送到服务器。在服 务器端,RMI系统接收调用,并连接到一个框架上,而框 架则负责解除参数编组并在服务器上调用该方法的实现 。当服务器的执行完成后,无论返回一个值或抛出一个例外,框架通过对结果进行编组,并向客户机的存根发 送一个应答。存根解除应答编组,并根据需要返回一个 值或抛出一个例外。存根和框架是用服务器的实现生成 的,通常使用的是程序rmic。存根使用引用与框架进行会话。这种体系结构使引用能够定义通信的属性。用于 UnicastRemoteObject服务器的引用与在特定主机和端口上运 行的单个服务器对象进行通信。凭借存根/引用的分离功能,RMI就能添加新的引用类型。处理复制服务器的引 用可将服务器请求多路发送给一组正确的复制程序,汇 集响应,再根据多种响应返回正确的结果。如果服务程 序没有在虚拟机上运行,则另一个引用类型可激活该程序。客户机可透明地与所有这些引用类型共同工作。
4.保密与安全
在执行RMI请求时,安全是绝对有保证的。RMI可在客 户机与服务器之间提供安全信道,并可将下载的执行程序放入安全的“沙箱(sandbox)”中运行,从而保护您的系 统免遭不明客户机可能的攻击。
首先,必须定义您的安全需求,这非常重要。如果您 正在安全的企业网内部执行诸如ComputeServer这样的程序,则您只需知道谁在使用计算环路即可,这样您就能对 滥用系统的人进行跟踪。如果您需要提供商业计算服务 器,则您就需要防止多种恶意的破坏。这些都会影响接 口的设计--在企业内部,您可能只要求每个Task对象都配备人名和部门编号,以便跟踪。而在商业领域中,您 可能需要更高的安全性,包括数字签名身份识别和某些 能帮助您剔除会耗费超过分配于其时间的恶意任务的合 同语言。
在客户机和服务器之间您可能需要有一个安全信道。 RMI可使您能提供可创建包括加密插口(socket)在内的您所需要的任何类型插口的插口工厂。从JDK 1.2开始,您将 能够指定服务器插口所提供的服务的要求(通过给出对 这些要求的描述)。这种新技术可在小应用程序上采用 ,而多数浏览器都拒绝承诺设置插口工厂。插口要求可包括加密和其它要求。
下载的类也存在安全问题。Java通过SecurityManager对象 处理安全问题,而该对象可传递所有与安全有关操作的判断,如打开文件和网络连接等。RMI通过要求您在输出 任何服务对象或在服务器上调用任何方法之前安装安全 管理器,以使用这个标准的Java机制。RMI提供与小应用程序(无文件存取,只是连接到发出主机等)限制相同的 RMISecurityManager类型。这样可防止下载的执行程序从计算 机上读取或写入数据、或与防火墙后面的其它系统连接。您也可编写和安装自己的安全管理程序对象,以执行 不同的安全限制。
5.结论
RMI为真正面向对象的分布式计算提供了可靠的平台。 您可使用RMI连接到Java组件,或用其它语言编写的现有的组件。随着Java在您的环境中所具备的重要性的日益 增加,您还可扩大Java的使用范围,并获得所有的好处 --无需移植、低维护成本和安全而保密的环境。RMI为您提供了循序渐进地将Java扩展到您的系统所有部分的平 台,您可根据需要适时地添加Java服务器和客户机。只 要您添加了Java,那么它所有的好处都会随之而来。RMI 则使之更简单、保密和强大。
三.Swing GUI图形化用户界面技术的介绍
1. Swing简介
Swing的产生主要原因就是AWT不能满足图形化用户界面发展的需要。
AWT设计的初衷是支持开发小应用程序的简单用户界面。例如AWT缺少剪贴板、打印支持、键盘导航等特性,而且原来的AWT甚至不包括弹出式菜单或滚动窗格等基本元素。
此外AWT还存在着严重的缺陷,人们使AWT适应基于继承的、具有很大伸缩性的事件模型,基于同位体的体系结构也成为其致命的弱点。
随着发展的需要,Swing出现了,Swing组件几乎都是轻量组件,与重量组件相比,没有本地的对等组件,不像重量组件要在它们自己的本地不透明窗体中绘制,轻量组件在它们的重量组件的窗口中绘制。
Swing是由100%纯Java实现的,Swing组件是用Java实现的轻量级( light-weight)组件,没有本地代码,不依赖操作系统的支持,这是它与AWT组件的最大区别。由于AWT组件通过与具体平台相关的对等类(Peer)实现,因此Swing比AWT组件具有更强的实用性。Swing在不同的平台上表现一致,并且有能力提供本地窗口系统不支持的其它特性。
Swing采用了一种MVC的设计范式,即"模型-视图-控制"(Model-View-Controller),其中模型用来保存内容,视图用来显示内容,控制器用来控制用户输入。
Swing外观感觉采用可插入的外观感觉(Pluggable Look and Feel,PL&F)
在AWT组件中,由于控制组件外观的对等类与具体平台相关,使得AWT组件总是只有与本机相关的外观。Swing使得程序在一个平台上运行时能够有不同的外观。用户可以选择自己习惯的外观.
java.awt.Component
-java.awt.Container
-java.awt.Window
-java.awt.Frame-javax.swing.JFrame
-javax.Dialog-javax.swing.JDialog
-javax.swing.JWindow
-java.awt.Applet-javax.swing.JApplet
-javax.swing.Box
-javax.swing.Jcomponet
Swing包是JFC(Java Foundation Classes)的一部分,由许多包组成,如下表:
包 描述
Com.sum.swing.plaf.motif 用户界面代表类,它们实现Motif界面样式
Com.sum.java.swing.plaf.windows 用户界面代表类,它们实现Windows界面样式
Javax.swing Swing组件和使用工具
Javax.swing.border Swing轻量组件的边框
Javax.swing.colorchooser JcolorChooser的支持类/接口
Javax.swing.event 事件和侦听器类
Javax.swing.filechooser JFileChooser的支持类/接口
Javax.swing.pending 未完全实现的Swing组件
Javax.swing.plaf 抽象类,定义UI代表的行为
Javax.swing.plaf.basic 实现所有标准界面样式公共功能的基类
Javax.swing.plaf.metal 用户界面代表类,它们实现Metal界面样式
Javax.swing.table Jtable组件
Javax.swing.text 支持文档的显示和编辑
Javax.swing.text.html 支持显示和编辑HTML文档
Javax.swing.text.html.parser Html文档的分析器
Javax.swing.text.rtf 支持显示和编辑RTF文件
Javax.swing.tree Jtree组件的支持类
Javax.swing.undo 支持取消操作
swing包是Swing提供的最大包,它包含将近100个类和25个接口,几乎所有的Swing组件都在swing包中,只有JtableHeader和 JtextComponent是例外,它们分别在swing.table和swing.text中。
swing.border包中定义了事件和事件监听器类,与AWT的event包类似。它们都包括事件类和监听器接口。
swing.pending包包含了没有完全实现的Swing组件。
swing.table包中主要包括了表格组建(JTable)的支持类。
swing.tree同样是JTree的支持类。
swing.text、swing.text.html、swing.text.html.parser和swing.text.rtf都是用于显示和编辑文档的包。
Swing是AWT的扩展,它提供了许多新的图形界面组件。Swing组件以"J"开头,除了有与AWT类似的按钮(JButton)、标签(JLabel)、复选框(JCheckBox)、菜单(JMenu)等基本组件外,还增加了一个丰富的高层组件集合,如表格(JTable)、树(JTree)。
1)MVC(Model-View-Control)体系结构
Swing胜过AWT的主要优势在于MVC体系结构的普遍使用。在一个MVC用户界面中,存三个通讯对象:模型、视图和控件。模型是指定的逻辑表示法,视图是模型的可视化表示法,而控件则指定了如何处理用户输入。当模型发生改变时,它会通知所有依赖它的视图,视图使用控件指定其相应机制。
在Swing组件中视图和控件两部分合为一体。每个组件有一个相关的分离模型和它使用的界面(包括视图和控件)。比如,按钮JButton有一个存储其状态的分离模型ButtonModel对象。组件的模型是自动设置的,例如一般都使用JButton 而不是使用ButtonModel 对象。另外,通过Model类的子类或通过实现适当的接口,可以为组件建立自己的模型。把数据模型与组件联系起来用setModel( )方法。
2)可存取性支持
所有Swing组件都实现了Accessible接口,提供对可存取性的支持,使得辅助功能如屏幕阅读器能够十分方便的从Swing组件中得到信息。
3)支持键盘操作
在Swing组件中,使用JComponent类的registerKeyboardAction()方法,能使用户通过键盘操作来替代鼠标驱动GUI上Swing组件的相应动作。有些类还为键盘操作提供了更便利的方法。
其实这就相当于热键,使得用户可以只用键盘进行操作。
4) 设置边框
对Swing组件可以设置一个和多个边框。Swing中提供了各式各样的边框供用户选用,也能建立组合边框或自己设计边框。一种空白边框可以增大组件,协助布局管理器对容器中的组件进行合理的布局。
5) 使用图标(Icon)
与AWT的部件不同,许多Swing组件如按钮、标签,除了使用文字外,还可以使用图标修饰自己
三、 Java Web Start
Java Web Start(以下简称JWS)是SUN提供的一种通过Web来部署和发布Java 程序的新技术,它既可以用来发布Application,也可以用来发布Applet,它获去年全球Java技术最佳创意奖。它仅在第一次运行时下载程序,以后的事情,就全全交给JWS,包括版本的自动更新和维护。这是我们曾经梦寐以求的事情,程序运行在客户端(本地运行,当然有足够的速度),但不用去安装配置客户端,也不用去考虑版本升级后对客户端的维护,这就是JWS提供给我们的好处之一。OK,下面我们就来看看如何玩转JWS,本文仅用发布Application来做说明。
1.JWS简介
JWS主要用来通过网络部署你的应用程序,它具有安全、稳定、易维护、易使用的特点。用户访问用JWS部署应用程序的站点,下载发布的应用程序,既可以在线运行,也可以通过JWS的客户端离线运行已下载的应用程序。对同一个应用程序,在第一次运行时下载,以后每次运行时,JWS的客户端会自动去探测是否有版本更新,有更新就自动下载新版本,没有更新就直接运行本地当前版本,所有的麻烦全由JWS去承担。好,下面我们就一步一步来搭建JWS
2.搭建支持JWS的Web站点
第一步:你的Tomcat3.2.1已经正常运转
第二步:找到TomcatHOME/conf下的web.xml文件,在其中添加<mime-type>
application/x-java-jnlp-file
</mime-type>
以支持JNLP文件。
3.部署应用程序
第一步:开发你希望发布的应用程序
第二步:把应用程序及所用到的所有资源打成一个或多个jar包
第三步:如果你的应用程序不会用到任何运行这个应用程序的机器的本地资源,那么,你的应用程序就可以部署了。
第四步:如果你的应用程序用到了运行这个应用程序的机器的本地资源,那么,你的应用程序就必须先签名然后才可以发布。
第五步:如何给应用程序签名
1):首先确保你已经完全安装了Java2的环境,有keytool工具,它位于J2SE SDk的bin目录下。这一般不会有问题。
2):到Dos状态下,进入你需发布应用程序的jar包所在的目录,运行下面这句话
keytool -genkey -keystore myKeystore -alias jwstest
它将会提示你输入用户名、密码等,不用理它,按照提示随便输入即可,但一定要记住密码。运行结束它将会在当前路径下创建名为myKeystore的文件。
3):如果你想察看一下刚才生成的myKeystore文件的内容,可以使用下面这句话:
keytool -list -keystore myKeystore
显示出来应该类似如下:
Keystore type: jks
Keystore provider: SUN
Your keystore contains 1 entry:
jwstest, Tue Nov 23 19:29:32 PST 2001, keyEntry,
Certificate fingerprint (Test):
C3:A9:CD:F3:D3:AC:4D:3F:3C:5B:AF:9E:CF:0D:46:5C
4):对你需发布应用程序的jar包进行签名,运行下面这句话:
jarsigner -keystore myKeystore yourtest.jar jwstest
其中yourtest.jar是你的jar包名,你需要修改它,别的就不必修改了。运行时会提示你输入密码,就是你刚才在生成myKeystore文件时设定的密码。
第六步:部署应用程序的jar包。
1:在Tomcat的webapps中新建目录JWSTest
2:在JWSTest下新建目录apps,META-INF,WEB-INF
3:在apps下新建目录images和lib
4:在META-INF中拷入MANIFEST.MF
5:在WEB-INF中拷入web.xml
6:把已经准备好的jar包拷入lib目录下
Jsp页面
第一步:编写用于Web访问的页面index.jsp如下:
<%@page contentType="text/html;charset=gb2312"%>
<html>
<title>JWS Test</title>
<head>
<SCRIPT LANGUAGE="Javascript">
function insertLink(url, name) {
document.write("<a href=" + url + ">" + name + "</a><br><br>");
}
insertLink("http://你的IP:8080/ JWSTest /apps/JWSTest.jnlp"," JWSTest ");
</SCRIPT>
</head>
<body>
</body>
</html>
第二步:在jsp中添加检测访问的客户端是否已经安装了JWS的客户端的代码,示例如下:
<%@page contentType="text/html;charset=gb2312"%>
<html>
<title> JWS Test </title>
<head>
<SCRIPT LANGUAGE="Javascript">
var javawsInstalled = 0;
isIE = "false";
if (navigator.mimeTypes && navigator.mimeTypes.length) {
x= navigator.mimeTypes['application/x-java-jnlp-file'];
if (x) javawsInstalled = 1;
} else {
isIE = "true";
}
function insertLink(url, name) {
if (javawsInstalled) {
document.write("<a href=" + url + ">" + name + "</a><br><br>");
} else {
document.write("<a href=" + url + ">"+ name +"</a><br><br>");
}
}
</SCRIPT>
<SCRIPT LANGUAGE="VBScript">
on error resume next
If isIE = "true" Then
If Not(IsObject(CreateObject("JavaWebStart.IsInstalled"))) Then
javawsInstalled = 0
Else
javawsInstalled = 1
End If
End If
</SCRIPT>
<SCRIPT LANGUAGE="Javascript">
if(javawsInstalled){
insertLink("http://你的IP:8080/ JWSTest /apps/JWSTest.jnlp"," JWSTest ");
}else{
//通知用户要先安装JWS的客户端,你可以自己提供下载或是直接链接到Sun的JWS下载。
//分为两种,如果客户端已经安装了Java运行环境,则只要下载javaws-1_0_1_01-win-int.exe即可。
//如果客户端没有安装Java运行环境,则要下载完整的javaws-1_0_1_01-win-int-rt.exe。
}
</SCRIPT>
</head>
<body>
</body>
</html>
JNLP文件
第一步:下面我们来编写JWS的核心配置文件JNLP,有了它,才能将以上各部分联系起来,真正让JWS运转起来。JNLP文件符合标准的XML语法,实质就是一个XML文件。当然,编写它的最好方式是对已写好的JNLP进行改写。JWSTest.jnlp示例如下:
<?xml version="1.0" encoding="utf-8"?>
<!-- JNLP File for SwingSet2 Demo Application -->
<jnlp
spec="1.0+"
codebase="http://你的IP:8080/JWSTest/apps"
href=" JWSTest.jnlp">
<information>
<title> JWS Test </title>
<vendor>YOUR Name</vendor>
<homepage href=" JWSTest.html"/>
<description> JWS Test </description>
<icon href="images/ JWSTest.jpg"/>
<offline-allowed/>
</information>
<security>
<all-permissions/>
</security>
<resources>
<j2se version="1.3"/>
<jar href="lib/ JWSTest.jar"/>
</resources>
<application-desc main-class="运行启动的主类"/>
</jnlp>
第二步:部分JNLP的关键语法
<jnlp>元素
spec:必须是1.0及以上版本,这里用1.0+,不需修改。
codebase:资源的URL,是JNLP指向各连接的起始处,需自行修改。
Href:JNLP文件相对codebase的存放位置,和JNLP文件的全名,需自行修改。
<infomation>元素
Title:发布的应用程序简单标题,需自行修改。
Vendor:发行商信息,可以写上你的大名,需自行修改。
Homepage:存放有关应用程序的相关文档的URL,如help文件等,可有可无。
Description:对应用程序的描述,可以有多对<description></description>,可有可无。
Icon:用户下载你的应用程序后,在JWS里显示的图标的URL,应是gif或jpeg格式。需自行修改。
Offline-allowed:选择项,允许用户离线运行应用程序,一般都会有,不用修改。
<security>元素
选择项,如果没有指明<security>,默认是不允许应用程序访问用户的本地资源,即应用程序是沙箱运行。
如果设定为<all-permissions/>,则表示允许应用程序访问用户的本地资源。一般都会设定此值。
<resource>元素
<j2se version = 指定jdk版本>
<jar href = 指定需发布的应用程序的jar包存放的位置>
<application-desc>元素
main-class:应用程序运行启动的主类
<argument>:应用程序运行时的参数,可以有多个,每一个参数用一对<argument>参数</argument>。
至此,你已经完全构建了运转JWS的各部件。
完整发布和测试
前面我们已经准备好了需发布的应用程序的jar包,也写好了用来访问的jsp文件和服务器端的核心jnlp文件。
第一步:在JWSTest下新建目录jsp。把index.jsp拷入jsp目录。
第二步:把jnlp文件直接拷入apps目录下。
第三步:在浏览器里输入:http://localhost:8080/JWSTest/jsp/index.jsp 即可访问到jsp页面。页面应出现JWSTest字样。
第四步:点击JWSTest,连接到apps下的JWSTest.jnlp文件,JWS启动,开始下载你发布的应用程序。
第五步:下载完毕,直接运行即可。以后,你也可以直接运行JWS客户端里已下载的应用程序。
不出意外,应恭喜你已经开始享受JWS带来的乐趣了。
四、 程序设计分析
1. 软件设计需求分析
管理员 |
树节点编辑 |
数据的查询 |
添加 |
删除 |
修改 |
平尾 |
AO |
长驻AO |
零件 |
用戶 |
登陆 |
可编辑界面 |
浏览界面 |
1. 业务功能实现
经调研,波音结构管理树主要实现如下功能:
1) 登陆界面,用户登陆后根据身份不同分管理者界面和普通用户浏览界面,普通用户只能简单的查看数据和查询数据,而管理者可以对树进行完全操作:添加,修改,删除。
2) 主树状界面,在此界面管理者可以对树进行实时编辑,编辑结果将反映在界面上。普通用户可以浏览次树,并且可以查看产品的信息,双击产品图标可以跳转到产品相关网页
3) 查询界面,在此界面上用户可以根据平尾,AO,长驻AO,AO,零件进行数据查询,查询支持多条件与或查询,还支持模糊查询。查询结果将实时反映在界面上,并且用户双击查询结果可以跳转到此产品的详细资料网页上去。
2. 程序基本构架
1) RMI技术的应用
系统采用先进的RMI(远程方法调用技术)把系统一分为二:服务器端程序和客户端程序。
一. 客户端程序利用SWNIG技术主要实现图形化用户界面(GUI),获取客户信息提交给服务器端进行处理,并反馈服务器端处理的结果
二. 服务器端程序主要提供程序接口给客户端调用,所以服务器端RMI服务必须始终启动,客户端才能正常运行
三. 服务器端程序再连接后台数据库对数据库进行存取,查询,更新,删除等操作。
2) SWING技术的应用,客户端用户界面都是用SWING实现的,
SWING是真正采用MVC构架的设计模式,不但构架清晰直白,而且非常利于程序的扩展。
3) 使用Java web start来部署WEB应用程序高效,开销小,低维护 java web start的自动更新下载和自身的沙漏(sandBox)模式是程序安全,方便。
五、 程序详细设计
1. 数据库的设计
根据树的层次结构:平尾->站位->AO->长驻AO->零件,数据库建了四个表:AoStandTable,AoTreeTable, AoNumTreeTable, PartTreeTable.
1)AoStandTable主要用来存贮站位信息:
字段 | 类型 | 说明 |
AoNum | nchar(10) | AO号 |
Version | nchar(10) | 版次 |
CN_Nam | nchar(20) | 中文名称 |
EN_Name | nchar(20) | 英文名称 |
StartNum | int(4) | 起始架次 |
EndNum | Int(4) | 终止架次 |
StandNum | nchar(10) | 站位 |
SideNum | nchar(10) | 侧位 |
2)AoNumTreeTable,主要存贮AO信息
字段 | 类型 | 说明 |
AoNum | nchar(10) | AO号 |
StandNum | nchar(10) | 站位 |
CN_Name | nchar(20) | 中文名称 |
EN_Name | nchar(20) | 英文名称 |
StartNum | int(4) | 起始架次 |
EndNum | int(4) | 终止架次 |
SideNum | nchar(10) | 侧位 |
3) AoTreeTable,主要用来存贮长驻AO信息
字段 | 类型 | 说明 |
AoNum | nchar(10) | AO号 |
Version | nchar(10) | 版次 |
StandNum | nchar(10) | 站位 |
CN_Name | nchar(20) | 中文名称 |
EN_Name | nchar(20) | 英文名称 |
StartNum | int(4) | 起始架次 |
EndNum | int(4) | 终止架次 |
SideNum | nchar(10) | 侧位 |
4) PartTreeTable,主要用来存贮零件信息
字段 | 类型 | 说明 |
ParentTreePath | nvarchar(100) | 父节点的路径 |
TreePath | nvarchar(100) | 节点路径 |
AoNum | nchar(20) | AO号 |
PartNum | nchar(20) | 零件号 |
ParentNum | nchar(20) | 父节点号 |
PartNameCN | nchar(20) | 零件中文名称 |
PartNameEN | nchar(20) | 零件英文名称 |
Quantity | int(4) | 数量 |
Type | nchar(20) | 类型 |
MainMakeDept | nchar(20) | 主要制造部门 |
SubMakeDept | nchar(20) | 次要制造部门 |
UseDept | nchar(20) | 使用部门 |
StartNum | int(4) | 起始架次 |
EndNum | int(4) | 终止架次 |
StandNum | nchar(20) | 站位 |
2,服务器端设计
1)编写远程接口
下面的代码显示了一个简单远程接口的接口定义
这些方法必须能抛出 RemoteException,如果客户机和服务器之间的通信出错,则客户机将捕获此异常。
注:该接口本身继承了 java.rmi 包中定义的 Remote 接口。Remote 接口本身没有定义方法,但通过继承它,我们说明该接口可以被远程地调用。
这里是编写远程接口的代码
import java.rmi.*; import java.rmi.registry.*; import java.sql.*; import java.util.List; import java.util.Vector;
public interface TreeServerInterface extends Remote { boolean checkUser(String username,String password) throws RemoteException,SQLException; Vector getAoNum(String standNum) throws SQLException,RemoteException; AoDocument getAo(String aoNum) throws SQLException,RemoteException; PAoDocument getPAoNum(String aoNum,String version) throws SQLException,RemoteException; List getPermanentAoNum(String aoNum) throws SQLException,RemoteException; ….. }
|
2) 实现远程接口
treeServerR具体实现了上面接口提供的方法
import java.rmi.server.*; import java.rmi.*; import java.rmi.registry.*; import samf.conn.SfConn; import java.sql.*; import java.util.*;
public class treeServerR extends UnicastRemoteObject implements TreeServerInterface { public treeServerR() throws RemoteException { super(); } public boolean checkUser(String username,String password) throws SQLException { String sql="select * from users where username='"+username+"'"; SfConn conn=new SfConn(); ResultSet rs=conn.executeQuery(sql); if(rs.next()) { String pass=rs.getString("password"); rs.close(); //conn.Close(); if(pass.equals(password)) return true; else return false; } else return false; }
public List getStandNum() throws SQLException { List result=new ArrayList(); String sql="select * from AoStandTable "; SfConn conn=new SfConn(); ResultSet rs=conn.executeQuery(sql); while(rs.next()) { String standNum=rs.getString("StandNum"); result.add(standNum); } rs.close(); conn.Close(); return result; } |
3)编写 RMI 服务器
服务器 main 通常要做的步骤是:
1.安装安全性管理器类,它允许服务器程序从其它机器接收存根类
2.创建服务器对象的实例
3.服务器对象注册到 RMI 命名注册表以便客户机程序能找到该服务器对象
1)设置安全性管理器
第一步是安装 RMI 安全性管理器。尽管这不是严格必须的,但它确实允许服务器虚拟机下载类文件。例如,假设客户机调用服务器中的方法,该方法接受对应用程序定义的对象类型(例如 BankAccount)的引用。通过设置安全性管理器,我们允许 RMI 运行时动态地将 BankAccount 类文件复制到服务器,从而简化了服务器上的配置。
让 RMI 动态地下载这些类的弊端是有安全性风险。也就是说,实质上我们是在让服务器执行来自另一台机器的代码。虽然我们希望这些类文件不会危及服务器,但如果希望避免这样的风险,则您的 RMI 服务器不应该安装安全性管理器。然后,您必须确保将所有类文件安装在本地服务器的类路径中。
这里是用于设置安全性管理器的代码:
import java.rmi.*;
import java.rmi.server.*;
public static void main (String [] args ) throws
RemoteException, java.net.MalformedURLException,
RMISecurityException
{
System.setSecurityManager (
new RMISecuritymanager() );
MeetingServer ms = new MeetingServer();
Naming.rebind (
"rmi://myhost.com/Meeting", ms );
}
2)命名远程对象
服务器的下一步工作是创建服务器对象的初始实例,然后将对象的名称写到 RMI 命名注册表。RMI 命名注册表允许您将 URL 名称分配给对象以便客户机查找它们。要注册名称,需调用静态 rebind 方法,它是在 Naming 类上定义的。这个方法接受对象的 URL 名称以及对象引用。
名称字符串是很有趣的部分。它包含 rmi:// 前缀、运行 RMI 对象的服务器的计算机主机名和对象本身的名称,这个名称正是您所想要的。注:您可以调用由 java.net.InetAddress 类定义的 getLocalHost 方法,而不必象我们在这里所做的一样硬编码主机名。
这里是命名远程对象的代码:
import java.rmi.*;
import java.rmi.server.*;
public static void main (String [] args ) throws
RemoteException, java.net.MalformedURLException,
RMISecurityException
{
System.setSecurityManager (
new RMISecuritymanager() );
MeetingServer ms = new MeetingServer();
Naming.rebind (
"rmi://myhost.com/Meeting", ms );
}
3.客户端设计
客户端主要分这样大模块:登陆窗口,主窗口,查询窗口。
1. 登陆窗口的设计
登陆窗口比较简单就是一个对话框,要求输入用户名和密码,并进行验证,验证后根据不同用户身份跳转到不同的用户界面,下面是验证的代码:
void jButton1_actionPerformed()
{
String password=new String(jPassField.getPassword());
String username=jUserField.getText();
if(username.equals(""))
{
JOptionPane.showMessageDialog(null,"请输入用户名!","错误",JOptionPane.ERROR_MESSAGE);
}
else
if(password.trim().equals(""))
{
JOptionPane.showMessageDialog(null,"请输入密码!","错误",JOptionPane.ERROR_MESSAGE);
}
else{
try{
boolean flag=conn.checkUser(username,password);
if(flag)
{
//JOptionPane.showMessageDialog(null,"密码正确");
f.dispose();
//System.exit(0);
new MainFrame(username);
}
else
{
JOptionPane.showMessageDialog(null,"你的密码不正确,请重试!","错误",JOptionPane.ERROR_MESSAGE);
}
}catch(Exception remotee)
{
remotee.printStackTrace();
}
}
}
2. 主窗口的设计,
主窗口主要用到了这些SWING 组件:Jmenu,Jtree,Jtable,
其中Jmenu用来实现程序菜单,Jtree用来实现产品树状浏览,Jtable用来显示产品信息字段。
1) 菜单的实现:
菜单的一些代码如下:
stand=new JMenuItem("创建站位(S)");
standP=new JMenuItem("创建站位");
ao=new JMenuItem("创建AO(O)");
aoP=new JMenuItem("创建AO");
perpantAo=new JMenuItem("创建长驻AO(L)");
perpantAoP=new JMenuItem("创建长驻AO");
partButton=new JMenuItem("创建零件");
partButtonP=new JMenuItem("创建零件");
让菜单加上快捷键的代码如下:
stand.setAccelerator(KeyStroke.getKeyStroke('S',java.awt.Event.CTRL_MASK,false));
ao.setAccelerator(KeyStroke.getKeyStroke('O',java.awt.Event.CTRL_MASK,false));
perpantAo.setAccelerator(KeyStroke.getKeyStroke('L',java.awt.Event.CTRL_MASK,false));
modify.setAccelerator(KeyStroke.getKeyStroke('M',java.awt.Event.CTRL_MASK,false));
delete.setAccelerator(KeyStroke.getKeyStroke('D',java.awt.Event.CTRL_MASK,false));
2) Jtree的实现:
这是初始化树的一些代码:
public JTree loadTree() throws Exception
{
DefaultMutableTreeNode root=new DefaultMutableTreeNode(new IconData(ICON_ROOT,null,"平尾"));
DefaultMutableTreeNode standNode;
List standResult=conn.getStandNum();
BoeingProductStruct[] bps=new BoeingProductStruct[standResult.size()];
for(int i=0; i<standResult.size();i++)
{
String standNum=standResult.get(i).toString();
bps[i]=new BoeingProductStruct(standNum,"Stand");
standNode=new DefaultMutableTreeNode(new IconData(ICON_STAND,null,new BoeingProductNode(bps[i])));
root.add(standNode);
standNode.add(new DefaultMutableTreeNode(new Boolean(true)));
}
treeModel=new DefaultTreeModel(root);
tree=new JTree(treeModel)
{
public String getToolTipText(MouseEvent ev)
{
return "点击右键编辑节点";
}
};
ToolTipManager.sharedInstance().registerComponent(tree);
//tree.putClientProperty("JTree.lineStyle","Horizontal");
tree.putClientProperty("JTree.lineStyle", "Angled");
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
TreeCellRenderer renderer = new
IconCellRenderer();
tree.setCellRenderer(renderer);
tree.addTreeExpansionListener(new
DirExpansionListener());
if(canEdit)
tree.addMouseListener(new ClientTree_MouseAdapter(this));
tree.addTreeSelectionListener(new ClientTree_TreeSelectionListener(this));
tree.setShowsRootHandles(true);
return tree;
}
3) Jtable的实现:
这是Jtable的一些代码实现:
class StandTable extends AbstractTableModel
{
Object standProperties[][];
String[] columnName={"站位"};
int size;
public StandTable()
{
try{
List result=conn.getStandNum();
size=result.size();
standProperties=new Object[size][1];
for(int i=0;i<size;i++)
{
standProperties[i][0]=result.get(i);
}
}catch(Exception e)
{
e.printStackTrace();
}
}
public int getColumnCount() {
return columnName.length;
}
public int getRowCount() {
return size;
}
public String getColumnName(int col) {
return columnName[col];
}
public Object getValueAt(int row, int col) {
return standProperties[row][col];
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
}
4) 查询窗口的实现:
查询窗口将根据不同的方式查询数据,包括平尾,AO,长驻AO,零件,查询结果将列在Jtable中,
下面是它的一些代码实现:
class ProductModel extends AbstractTableModel {
String[] flyHead={"产品号","名称(中)","名称(英)","起始架次","终止架次","数量","类型","主要制造部门","次要制造部门","使用部门","版次"};
String[] aoHead={"AO号","名称(中)","名称(英)","起始架次","终止架次","站位","侧位"};
String[] paoHead={"长驻AO号","版次","名称(中)","名称(英)","起始架次","终止架次","站位","侧位"};
String[] partHead={"零件号","名称(中)","名称(英)","起始架次","终止架次","数量","类型","主要制造部门","次要制造部门","使用部门","所属AO"};
String type;
public ProductModel(String type){
this.type=type;
}
public int getRowCount()
{
return vect.size();
}
public int getColumnCount() {
if(type.equals("平尾"))
return flyHead.length;
else if(type.equals("AO"))
return aoHead.length;
else if(type.equals("长驻AO"))
return paoHead.length;
else if(type.equals("零件"))
return partHead.length;
return flyHead.length;
}
public String getColumnName(int c) {
if(type.equals("平尾"))
return flyHead[c];
else if(type.equals("AO"))
return aoHead[c];
else if(type.equals("长驻AO"))
return paoHead[c];
else if(type.equals("零件"))
return partHead[c];
return flyHead[c];
}
public Object getValueAt(int r, int c) {
if(!vect.isEmpty())
return((Vector)vect.elementAt(r)).elementAt(c);
else
return null;
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
}
}
上图为查询结果。
六、 软件的部署和运行
经过以上软件开发过程,系统已经大体出来了,最后要做的工作是软件的部署和运行,考虑到软件要和网页进行交互,比如点击产品链接的时候要跳转到产品相关网页,开始考虑使用:
try{
Process p=Runtime.getRuntime().exec("cmd /c start "+url);
}catch(Exception urle)
{
JOptionPane.showMessageDialog(null,"不支持打开网页");
}
但由于RunTime调用的是window的CMD程序,而且不同的系统CMD参数不一样,这样就不利于程序的移植,也违背了Java的“一次编写,到处运行”的优点,所以这种做法不可取。
综合各种因素考虑后,决定使用Java web start来部署程序,顾名思义,它的产生就是为了帮助Java应用程序能够方便的在网页上运行,它也确实名副其实的做到了这一点,它自带了丰富的和网页交互的方法,使程序员不废吹灰之力就能够做到程序和网页的交互操作:
try{
bs.showDocument(new java.net.URL(url));
}catch(java.net.MalformedURLException exp)
{
exp.printStackTrace();
}
只要以上简单几行代码就能实现在程序中打开网页,当然它的功能还不只这写,由于本文不是专门讨论Java web start技术的,有兴趣的读者可以去Sun网站参阅API和帮助
<?xml version="1.0" encoding="UTF-8"?>
<jnlp codebase="http://boeing.samf/boeing/BoeingTree/app">
<information>
<title>BoeingTree</title>
<vendor>SAMF Corporation</vendor>
<description>BoeingTree is a product manager</description>
<homepage href="http://boeing.samf/boeing/" />
<icon href="./../images/tree.gif"/>
<offline-allowed/>
</information>
<security>
<all-permissions/>
</security>
<resources>
<j2se version="1.2+"/>
<jar href="tree.jar"/>
</resources>
<application-desc main-class="treeServer.loginForm"/>
</jnlp>
對对JNLP(Java TM Network Launching Protocol)进行配置后,
程序就能够在网页上运行,而且能够智能更新!
上图展示了即使不登陆网站也能通过Java Web Start应用程序管理器使用软件!
七、 结束语
1,一些不足
波音结构管理树基本实现了客户的要求,但也有一些地方不足,
首先Swing程序界面的美观问题:
大家使用软件中可以发现,程序界面并不和我们通常用的Windows程序界面一样,是的,这是由于SWING的起源的原因。
即使它模拟出的Windows的风格也不尽人意:
SWING和AWT它们的外观总是和同一操作系统平台下的其它软件显得格格不入。对机器配置的需求也似乎永无止境,这使得它们只能被一些总是拥有当前最高性能PC的程序员们所容忍,或是那些不在乎金钱和时间的专业用户所接受。对绝大多数计算机使用者来说,AWT或SWING代表着怪异的界面和无法接受的速度。Standard Widget Toolkit(SWT)或许是Java这一噩梦的终结者,广大Java程序员终于可以开发出高效率的GUI程序,它们拥有标准的外观,几乎没有人能看出你的程序是用Java写出来的,更为重要的是,这些程序是跨平台的。
决定在下个版本中使用swt技术。
致谢
在笔者软件开发过程中得到了领导,师傅和同事们的许多帮助,没有他们的指导和鼓励是不会有这个软件的实现的。在此感谢他们!
参考文献
<<Thinking In java>>
IBM教程<<RMI 和 CORBA和分布式对象>>
<<精通swing程序设计>>Made by e-Stack Room
http://www.mycnknow.com