EclipseRCP技术和OSGi规范在客户端及服务器端的应用

导论 和其他行业一样,订做家具行业呈现出这样一个特点——日益变化的需求应当被反 映到从事该行业的公司使用的软件中。位于伊利诺斯州的芝加哥RPC Software公司在其产品 中通过使用开源软件从而在市场中获得了成功。该公司利用Eclipse RCP、DotProject以及 SugarCRM等技术快速地发布了一个更具有成本效益的解决方案, 从而击败了竞争对手。该案 例研究不但揭开了技术层面的面纱,而且总结了开发中获得知识以及经验教训。 业务

RPC Software公司为家具行业开发了ERP订单管理软件。在RPC的产品出现前,从该行业的 公司往往使用一些私权软件(proprietary software),这些软件基于微软Visual Studio 程序语言(如Visual Basic)、DOS解决方案和CA的Visual Object。如今的公司都在寻找能 够处理很多不同业务的解决方案,例如销售(sale)、报价(quote)、订单(order entry )、时间追踪(time tracking)、仓储(warehousing)、财务管理(accounting)和报表 (reporting)。 因此,能够满足这些需求的软件不仅能够不断升级,而且根本上也应该模 块化。

和其他很多行业一样,近年来有一个趋势(drive)——使信息更加透明并且更接近销售 者和定期与家具经销商打交道的客户。这个改变由两个方面进行驱动。一方面,从事于该产 业的公司纷纷转向开放数据交换格式,例如OFDA-XML。另一方面,业务流程(如项目跟踪) 使用Web应用呈报报表,这使得合作公司间共享信息成为可能。

解决方案概述

RPC Software公司的客户要求软件能够快速而明确地适应其业务需求。他们不但要求软件 拥有强大的客户端功能以便员工日常使用,而且要求软件具有为其他不同的层次业务和合作 者的呈报功能。考虑到客户的这些需求,RPC Software公司决定利用开源软件作为解决方案 的基础。RPC Software公司的产品线有一个基于Eclipse RCP和Apache tomat技术的ERP富客 户端/服务端组件,有一套基于Web的以开源DotProject PHP应用为基础的项目管理解决方案 ,还有一套即将发布的基于Web的以开源SugarCRM为基础的CRM产品。

对于项目管理和CRM产品,之所以选择基于web的解决方案,是因为不必安装胖客户端,就可 以在经销商、客户和销售者间共享信息。对于ERP产品,之所以选择Eclipse RCP是因为 SWT/JFace部件集提供了丰富的功能并且有OSGI提供了模块化基础。

Eclipse RCP是 基于OSGi规范构造的,这是其核心所在。维基百科给出的OSGi框架定义如下:

该框 架能实现一个完整的、动态的组件模型。而单独的Java VM环境正好缺少这个模式。应用程序 (称为bundle)无需重新引导可以被远程安装、启动、升级和卸载(其中Java包/类的管理被 详细定义)API中还定义了运行远程下载管理政策的生命周期管理。服务注册允许bundles去 检测新服务和取消的服务,然后相应配合。(http://zh.wikipedia.org/wiki/OSGi)(译者 注:节选自http://zh.wikipedia.org/wiki/OSGi)

该CORE产品由客户端和服务器端组成 。从功能组件中的代码组织到客户层和服务器层代码重用,都广泛地使用了OSGi。Eclipse RCP允许将Java类和资源文件模块化于jar文件中,这些jar文件为被称作插件(plugin)。插件 是OSGI包(OSGi bundle)的扩展集。RCP Software客户端按功能分为不同的核心业务插件。 RCP Software客户端同样使用了包含第三方API的插件,例如Hibernate和Jasper Reports。 CORE Business服务器端也是由一组插件构成。这就使在客户端和服务器端很容易地重用业务 逻辑插件成为可能。

为了进一步简化应用的部署,RPC应用已经将客户端和服务器端 打包在同一个安装包中。在一个插件中,Eclipse RCP通过结合XML和配置文件,定义了入口 点(entry point)概念。在众多插件中,框架利用依赖元数据以确定哪些插件需要从指定切 入点加以启动。对于客户端,基于常规的exe文件的Eclipse RCP应用通过客户端切入点来完 成启动过程。客户端启动时就会排除那些服务器端功能插件。相似地,当Eclipse RCP作为服 务器端而运行,JNIWrapper会建立Windows服务,此时它利用的是另一个入口点来启动 Eclipse RCP安装。安装中包含了Tomcat服务实例包,而UI逻辑插件或者客户端相关的插件( 如:SWT插件)都不会被安装。

Core Business客户端处理与通常的ERP相关的作业,例如设立提案、装载票据材料和财务 管理。服务器组件提供了基于web的数据报表的服务,这使公司机构成员间了解更高层次的汇 总报告成为可能。当订单通过Core Business客户端提交,在Core Vision产品数据库中会自 动创建一个产品id。Core Vision中的变化也将呈现在Core Business中。同样地,在Core Business中CRM的变化也会反映到CORE CRM中,反之亦然。RPC整合了两个数据库,而单独使 用DotProject和SugarCRM的公司则没法进行这样的整合。

Eclipse RCP 客户端

Core Business产品以Eclipse RCP富客户端框架应用为主。客户日常使用CORE Business 客户端以满足ERP的要求,例如财务管理(accounting)、报表管理(management reporting )、工程造价(project pricing)、设立提案(proposal)等等。与其他技术相比,选择 Eclipse RCP有很多理由。因为信息输入和客户数据量的要求,基于web的应用并不是可行的 选择。除了Eclipse RCP和SWT,备选的富客户端部件框架还有C#和Swing,但是本地化的外观 和感官是SWT的关键卖点。对RPC Software公司来说,封装于 Eclipse RCP内的功能(诸如窗 体、菜单和首选项)也是相当诱人之处。

OSGI和Eclipse RCP提供的模块化已经被RPC Software公司广泛地应用于CORE Business客 户端。客户通常需要例如定制报表和计算逻辑等功能,但并不是每一个客户端都需要像Time Entry这样的功能。基于插件的Eclipse RCP架构,RPC软件公司分发一系列的核心应用插件和 为客户特别定制的插件,这使其满足以上需求成为可能。

Eclipse RCP的插件使用xml文件来告知核心应用该插件有哪些用途。定制报表就是 RPC使用该功能的例子。下面的XML片段展示了在运行时,如何通过添加客户定制插件 custom.plugin.*.core来添加定制定购报表。

                  

该控制样式的优点在于, 在应用运行时功能所需的配置和元信息都包括在定制插件中。而核心插件或者菜单系统没有 必要知道新功能的存在。Eclipse RCP框架在运行时会就会发现和应用上述改变。

Eclipse服务器端

RPC Software公司不仅在CORE Business客户端使用了Eclipse RPC基于插件的架构,在基于Tomcat的CORE Business服务器端亦然。CORE Business服务器通 过基于CORE Business Eclipse RCP客户端来显示已输入的数据报表。设计CORE Business Server过程中,对于重用相同逻辑和客户端已有的诸如Hibernate和Jasper Reports组件的需 求越发明显。显而易见的需求重用的解决方案是将服务器端的Java类重新打包为jar文件,并 将此jar文件包含在WAR文件中。在这个解决方案中,随着Java类结构的改变,原本复杂的构 建脚本也需要不断的修正。实际采用的解决方案非常简单,就是让Tomcat变地对“插件 敏感”。

Eclipse IDE帮助系统使用了Tomcat的内嵌版本, 这为RPC Software公司设计其需求功能 提供了起点。基于这点,Servlet.jar文件被移到他们自己开发的插件里。这样,其他被创建 的依赖servlet API的插件就可以使用它。已有的Tomcat插件在修改后使用Eclipse JDT编译 器,并非标准的Java编译器。因此CORE Business只需捆绑JRE而不是JDK。最后,用来加载包 含JSP页面的插件的类装载器,改进后被用来加载必要的系统插件,例如 org.eclipse.core.runtime。

package org.eclipse.help.internal.appserver;public class PluginClassLoaderWrapper extends URLClassLoader {.../*** This is a workaround for the jsp compiler that needs to know the* classpath.*/public URL[] getURLs() {Set urls = getPluginClasspath(_plugin);return (URL[]) urls.toArray(new URL[urls.size()]);}private Set getPluginClasspath(String pluginId) {// Collect set of plug-insSet plugins = new HashSet();addPluginWithPrereqs(pluginId, plugins);// Collect URLs for each plug-inSet urls = new HashSet();for (IteraTor it = plugins.iteraTor(); it.hasNext();) {   String id = (String) it.next();   try {     Bundle b = Platform.getBundle(id);     if (b != null) {     // declared classpath     String headers = (String) b.getHeaders().get (Constants.BUNDLE_CLASSPATH);     ManifestElement[] paths =ManifestElement.parseHeader (Constants.BUNDLE_CLASSPATH, headers);     if (paths != null) {       for (int i = 0; i < paths.length; i++) {       String path = paths[i].getValue();       addBundlePath(urls, b, path);       }     } else {         // RPC custom code:       try {   String bundleJarPath = b.getLocation();         if (bundleJarPath.equals(Constants.SYSTEM_BUNDLE_LOCATION)) {           SystemBundle systemBundle = (SystemBundle) b;           bundleJarPath = ((SystemBundleData) systemBundle.getBundleData()).getBundleFile().getBaseFile().toURL().getPath ();           bundleJarPath =bundleJarPath.substring (bundleJarPath.lastIndexOf("plugins/"));         } else if(bundleJarPath.startsWith("initial@reference:file:")) {           bundleJarPath =b.getLocation().replaceFirst ("initial@reference:file:", "");         } else {           bundleJarPath =b.getLocation().replaceFirst("update@", "");         }         if (bundleJarPath.endsWith("/")) {           bundleJarPath = bundleJarPath.substring(0, bundleJarPath.lastIndexOf("/"));           }         if (bundleJarPath.startsWith("plugins/") && bundleJarPath.endsWith(".jar")) {           URL installURL = Platform.getInstallLocation().getURL ();           bundleJarPath = installURL.getPath() + bundleJarPath;           urls.add(new URL(installURL.getProtocol(), installURL.getHost(), bundleJarPath));           }         } catch (Exception ex) {         }      // RPC custom code:   }   // dev classpath   String[] devpaths =DevClassPathHelper.getDevClassPath(pluginId);   if (devpaths != null) {     for (int i = 0; i < devpaths.length; i++) {         addBundlePath(urls, b, devpaths[i]);     }   }   }} catch (BundleException e) {}}return urls;}// RPC custom code:private void addBundlePath(Set urls, Bundle b, String path) {URL url = b.getEntry(path);if (url != null) {   try {     urls.add(FileLocaTor.toFileURL(url));   } catch (IOException ioe) {   }}}// RPC custom code:...}

该解决方案较之传统的Java WAR安装部署有很多优点。首先,RPC Software公司现在能够 在客户端和服务器端使用相同的插件来提升重用和减少维护。其次,安装部署本质上也不复 杂。对于WAR文件,RPC必须在每个客户站点上安装Tomcat,并且为WAR部署。通过将服务器捆 绑成一组即用的Eclipse RCP插件,在获得可靠性的同时,能在每个客户端安装时节省大量的 配置和测试。

浏览器整合

CORE Business应用中报表兼容的发展过程也非常有意思。随着应用增多,为满足客户需 求很多报表工具都被选择使用,例如Apache FO、Jasper Reports和Standard Java Printing API。一些报表是基于服务器端,而另一些报表在客户端直接运行。客户希望在不切换浏览器 的前提下,能够浏览基于服务器端的报表。RPC能够通过使用嵌入浏览器组件的SWT来满足该 需求。只需一些类似于下面的代码,就能在基于Eclipse RPC的客户端上直接获得基于HTML的 报表,而这些报表都来自于服务器端。

final Browser browser = new Browser(shell, SWT.NONE);browser.setUrl("http://eclipse.org");

除了显示从服务器端获得的报表,客户也能轻松地使用例如下拉组合框来在客户端直接选 择报表的视图类型。随后,一个动态的URL生成,并且发送请求至服务器端。

回顾

总而言之,RPC Software公司已经发现Eclipse RCP不但可以满足其开发需求,而且是个 非常健壮的框架。它可以在维护单一代码的同时,满足客户的不同需求。开源代码从根本上 已经能够让其按需求增加所缺少的功能。基本上,JAVA能够通过使用众多API诸如Hibernate 、Apache FO和Jasper Reports进行快速地开发。

RPC Software公司非常满意Eclipse 作为Eclipse RCP开发的一个IDE。总之,PDE开发环 境能够让RCP开发像JAVA开发一样优秀。众多可做为插件使用的定制编辑器,使得手动编辑配 置文件更加方便。他们已经能够利用Eclipse IDE插件(例如Jasper Report GeneraTor插件 和Eclipse TPTP 插件)的优势来加快开发。

前景展望

在未来,RPC Software公司计划继续利用开源软件来增强其产品。如前所述,未来的CORE CRM产品通过利用SugarCRM提供基于web的一系列解决方案。他们同样也尝试着将用于CORE  Business的众多报表技术移植到Eclipse BIRT产品中。最终,通过Eclipse Update站点,将 产品更新和厂商目录分发到CORE Business客户端加以安装,这些将会下个版本的产品中实现 。

正如我总是意犹未尽的想起你。

EclipseRCP技术和OSGi规范在客户端及服务器端的应用

相关文章:

你感兴趣的文章:

标签云: