eclipse rcp
介绍
像其他许多行业一样,合同家具行业具有不断变化的要求,这些要求必须反映在行业公司所使用的软件中。 伊利诺伊州芝加哥市的RPC Software通过在其产品中使用开源软件的基础在该市场中获得了成功。 该公司正在利用Eclipse RCP,DotProject和SugarCRM等技术来提供比其竞争对手更快,更具成本效益的解决方案。 本案例研究对这些技术,使用它们所获得的开发知识以及所汲取的经验进行了深入研究。
域
RPC Software开发了家具行业使用的ERP订单管理软件。 在提供RPC产品之前,业内公司经常使用以Microsoft Visual Studio语言编写的专有软件,例如Visual Basic和基于DOS的解决方案以及CA的Visual Objects。 当今的公司正在寻找广泛的解决方案,以处理许多不同的业务活动,例如销售,报价,订单输入,时间跟踪,仓储,会计和报告。 结果,满足这种要求的软件不仅需要可扩展和强大,而且本质上还需要模块化。
像许多其他行业一样,近年来也一直在努力使信息更加透明,并使家具经销商定期与之互动的供应商和客户可以访问。 这种变化有两种主要驱动方式。 业内最早的公司正在转向开放数据交换格式,例如OFDA-XML。 其次,诸如项目跟踪之类的业务流程正在报告并通过Web应用程序公开,以允许合作公司共享信息。
解决方案概述
RPC Software的客户需要可以快速且专门适应其业务需求的软件。 他们还需要具有为每天使用该应用程序的员工提供强大的客户端功能的能力,以及将报表功能向其业务的其他层次和向合作伙伴公开的能力。 考虑到行业中客户的这些要求,RPC决定利用开源软件为其解决方案提供基础。 RPC的产品阵容包括使用Eclipse RCP和Apache Tomcat的富客户端ERP客户端/服务器组件,基于开源DotProject PHP应用程序的基于Web的项目管理解决方案,以及即将发布的使用开源SugarCRM的基于Web的CRM产品。作为其基础。
选择基于Web的解决方案作为项目管理和CRM产品的原因是,经销商,客户和供应商之间的需求共享,而无需安装胖客户端。 对于ERP产品,基于SWT / JFace小部件集的丰富功能以及其OSGi基础提供的模块化基础结构来选择Eclipse RCP。
Eclipse RCP的中心是建立在其上的OSGi规范。 根据Wikipedia的定义,OSGi框架提供:
该框架实现了完整的动态组件模型-在独立的Java / VM环境中缺少该模型。 可以远程安装,启动,停止,更新和卸载应用程序或组件(以捆绑形式出现),而无需重新启动-详细说明了Java包/类的管理。 生命周期管理是通过API完成的,这些API允许远程下载管理策略。 服务注册表允许捆绑软件检测新服务或服务消失,并进行相应调整。 ( http://en.wikipedia.org/wiki/OSGi )
CORE产品由客户端和服务器组成。 两者都广泛使用OSGi来将代码组织成功能组件,并在服务器和客户端层上重复使用该代码。 Eclipse RCP允许将类和资源模块化到称为插件的jar文件中,这些jar文件是OSGi捆绑包的超集。 RCP软件客户端针对其每个功能区域均分为核心业务插件。 它还利用了第三方API的插件,例如Hibernate和Jasper Reports。 CORE Business服务器也被构造为一组插件。 这允许在客户端和服务器上轻松重用业务逻辑插件。
为了进一步简化其应用程序的部署,RPC已将客户端和服务器结构化为包含在同一安装中。 Eclipse RCP定义了通过在插件中结合XML和配置文件定义的入口点的概念。 该框架使用插件之间的依赖元数据来确定需要为特定入口点启动哪些插件。 对于客户端,使用客户端入口点执行基于普通exe的Eclipse RCP启动过程。 此客户端启动不包括那些提供基于服务器功能的插件。 比较而言,当作为服务器运行时,将使用开源JNIWrapper产品创建Windows服务,该产品使用不同的入口点启动Eclipse RCP安装,该入口点包括Tomcat服务器实例软件包作为插件,但不包括包含UI特定逻辑或是客户端依赖项,例如SWT插件。
CORE Business客户处理与ERP相关的常见任务,例如创建提案,装载物料清单和会计。 服务器组件提供输入数据的基于Web的报告,这些报告使每个公司组织的成员都可以查看高级别的活动摘要。 通过CORE Business客户端输入订单时,还将在Core Vision产品的数据库中自动创建产品ID。 当在CORE Vision中进行时间表更改时,可以在核心业务中查看它们。 同样,在核心业务中进行的CRM更改将反映在CORE CRM中,反之亦然。 RPC处理两个数据库的集成。 单独使用DotProject和SugarCRM等产品的公司将无法访问此类集成。
Eclipse RCP客户端
CORE Business产品的核心是它对Eclipse RCP富客户端框架的使用。 客户每天都使用CORE Business客户端来满足ERP需求,例如会计,管理报告,项目定价,创建或建议等。出于许多原因,选择Eclipse RCP的原因是其他技术。 由于客户的信息输入和数据大小要求,基于Web的应用程序不是可行的选择。 除了Eclipse RCP和SWT之外,还考虑了诸如C#和Swing之类的富客户端窗口小部件框架。 SWT的原生外观和逼真度是关键卖点。 Eclipse RCP附带的开箱即用功能(例如,窗口,菜单和首选项)对于RPC软件也很有吸引力。
OSGi和Eclipse RCP提供的模块化已被CORE Business客户端中的RPC Software广泛使用。 客户经常需要诸如自定义报告和计算逻辑之类的项目。 在某些情况下,并非所有客户端都需要诸如“时间输入”之类的功能。 Eclipse RCP基于插件的基础结构允许RPC Software通过分发一组核心应用程序插件以及针对特定客户端需求的自定义插件来解决这些要求。
Eclipse RCP框架具有基于xml的格式,插件可使用该格式将其提供的功能通知核心应用程序。 RPC使用此功能的一个示例是自定义报告。 以下XML代码段显示了如何在运行时通过特定于客户的custom.plugin。*。core样式插件添加自定义采购订单报告。
<extension id= "xsltTransforms" point= "com.rpc.core.xsltTransforms" >
<xsltTransform id= "com.rpc.core.vendor.model.PurchaseOrder.pdf" >
<run class= "com.rpc.core.reporting.DefaultTransformSourceProvider" >
<parameter name= "location" value= "reports/PurchaseOrder.xsl" />
</run>
</xsltTransform>
</extension>
这种控制方式的价值在于,自定义插件中包含了所有配置更改和在运行的应用程序中包含功能所需的元信息。 无需进行任何更改即可使应用程序的核心插件或菜单系统意识到新功能。 Eclipse RCP框架在运行时发现了所有内容并将其连接在一起。
Eclipse服务器
RPC软件不仅在CORE Business客户端中而且在基于Tomcat的CORE Business服务器中都使用Eclipse RPC的插件结构。 CORE Business服务器公开了与通过基于CORE Business Eclipse RCP的客户端输入的数据相关的报告功能。 在设计CORE Business Server时,很明显,需要重用客户机上也存在的许多相同的逻辑和组件,例如Hibernate和Jasper Reports。 对重用要求最明显的解决方案是将服务器上需要的类重新打包为WAR文件中包含的jar。 该解决方案将需要复杂的构建脚本,随着类结构的改变,这些脚本需要不断进行修改。 相反,让Tomcat“知道插件”似乎容易得多。
Eclipse IDE将Tomcat的嵌入式版本用于其帮助系统。 RPC能够以此为起点来实现所需的功能。 此时,他们将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-ins
Set plugins = new HashSet();
addPluginWithPrereqs(pluginId, plugins);
// Collect URLs for each plug-in
Set 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现在能够在客户端和服务器层上使用完全相同的插件,从而促进重用并减少维护。 其次,部署设置本质上也没有那么复杂。 作为战争文件,RPC必须在每个客户端站点上安装Tomcat并配置WAR以进行部署。 通过将它们的服务器捆绑为Eclipse RCP插件的交钥匙集,他们可以在每次安装客户端时节省大量配置和测试,同时获得可靠性。
浏览器整合
核心业务应用程序的另一个有趣的元素是其报告功能的发展。 随着应用程序的增长,许多报告选项已用于满足客户需求,例如Apache FO,Jasper Reports和标准Java打印API。 尽管某些报告基于服务器,而其他报告可以直接在客户端中运行,但是客户希望能够查看基于服务器的报告而不必切换到浏览器。 RPC通过利用SWT嵌入式浏览器组件来满足这一需求。 使用几行类似于下面所示的代码,他们能够直接在基于Eclipse RPC的客户端中从服务器呈现基于HTML的报告。
final Browser browser = new Browser(shell, SWT.NONE);
browser.setUrl("http://eclipse.org");
客户端除了显示来自服务器的报告外,还包括易于使用的功能,例如下拉组合框,可直接在客户端中选择报告视图类型。 然后生成一个动态URL,并将请求发送到服务器。
得到教训
总体而言,RPC软件发现Eclipse RCP是满足其开发需求的非常强大的框架。 它使他们能够在保持单一代码库的同时满足客户的不同需求。 作为自然界中的开源,它们还可以根据需要增强缺少的功能。 总体而言,Java通过使用诸如Hibernate,Apache FO和Jasper Reports之类的API,可以更快地进行应用程序开发。 他们估计,如果不使用Eclipse RCP,则最初的应用程序开发将花费30个月而不是14个月。
RPC Software也很满意Eclipse作为Eclipse RCP开发的IDE。 通常,PDE开发环境使RCP开发成为与Java开发相当的一流活动。 插件功能支持文件的许多自定义编辑器降低了如果需要手动编辑配置文件的情况下的进入障碍。 他们还能够利用Eclipse IDE插件(例如Jasper Report Generator和Eclipse TPTP插件)来加快开发活动。
未来发展方向
RPC计划在将来继续利用开源软件来增强其产品。 如前所述,他们即将推出的CORE CRM产品将通过SugarCRM提供基于Web的CRM访问其解决方案套件。 他们还正在考虑将CORE Business产品中使用的各种报告技术迁移到Eclipse BIRT产品。 最后,将在该产品的下一版本中完成工作,以通过Eclipse Update站点功能向CORE Business客户端安装分发产品更新和供应商目录。
eclipse rcp