赞
踩
1、引言
Globus Toolkit 3.0(GT3)是Globus组织对全球网格论坛GGF(Global Grid Forum)制定的开放网格服务基础设施OGSI(Open Grid Service Infrastructure)的具体参考实现。GT3提供了一组类库和工具以帮助开发者开发符合开放网格服务体系结构OGSA(Open Grid Service Architecture)的网格服务程序。开发者在使用GT3开发网格服务时,需要使用文本编辑器编辑Java源文件和各种配置文件,并用Ant编译和配置这些文件,最后在控制台下输入命令运行服务。这使得开发网格服务变得很复杂,用户需要考虑东西太多。虽然GT3提供了图形化的网格服务查看和运行工具,但由于Java语言本身不论是AWT还是SWING在开发图形界面程序上的缺陷,使得开发出的GUI程序无论是速度还是外观,都让人难以接受。
Eclipse是新一代的IDE开发环境的代表,它不仅支持Java语言的开发,同时还可以支持其他语言的开发。Eclipse的核心是采用了插件的体系结构,通过开发插件,开发者可以在Eclipse上扩充可以想象的到的功能。Eclipse的Java开发工具(JDT)不仅支持Java语法显亮,还有Code Assist等功能。同时还集成了Ant插件,使得Eclipse开发Java程序变得非常容易。
标准窗口小部件工具箱Standard Widget Toolkit(SWT)是Eclipse组织为广大java程序员开发的基于Java的GUI程序所做出的一大贡献。SWT 工具箱类似于 SWING和AWT,它向开发人员提供了一组窗口小部件。然而,SWT 与其它工具箱之间的主要区别在于 SWT 通过Java本地接口(JNI)技术来调用操作系统的底层 GUI 窗口小部件。这种方法向 Java 开发人员提供了跨平台的 API 来实现"外观"像本机桌面应用程序的解决方案。
基于Eclipse和SWT,本文实现了一个具有通知机制的EchoString网格服务,并开发了一个GUI界面程序用来管理和运行网格服务。EchoString服务的功能很简单:客户端订阅该项服务,并在调用此服务时指定一个字符串,服务器端服务收到字符串后把该字符串发送给订阅此服务的用户,这个服务用到了GT3的通知机制。使用SWT开发的系统界面如下图:
图1 系统界面
本文假定你对网格服务有一定的了解,并能使用GT3编写一些基本的网格服务。可参看参考资料。
2、环境配置
操作系统:Windows 2000 professional
软件:
2.1 GT3的安装
安装GT3首先需要安装JDK和Apache Ant工具。请参看参考资料5。下载GT3-core-src.tar后,解压缩到一个目录,本文解压缩到了D://grid//ogsa//impl//java目录下。然后在此目录下运行ant dist来安装GT3。这需要等一段时间,视你的机器性能而定。安装完成后,在此目录下运行ant startContainer,然后重新打开一个cmd窗口到这个目录下,运行ant gui,如果有界面成功显示,表示GT3 core安装成功。
2.2 Eclipse的安装
Eclipse的安装很简单,只需要把它解压缩到一个目录就可以了。本文解压缩到了D://Eclipse。直接运行eclipse.exe,第一次运行的时候会自动寻找JDK并完成相应的配置,然后就进入了Eclipse的主界面。关于Eclipse的基本使用方法,IBM DdeveloperWorks,Eclipse的网站以及Eclipse的随机文档上都有详细地介绍,这里不详细描述了。
以下不加说明的话,GT3的目录为D://grid//ogsa//impl//java;Eclipse的目录为D://Eclipse。
3、使用Eclipse和GT3进行网格服务开发
3.1 建立工程
打开File->New->Java->Java Project,创建一个名为sample的Java工程。如图:
图2 建立工程
点击Next,进入下一个对话框,接收默认设置,然后点击Finish,这样一个Java工程的框架就自动生成了。
3.2 设置类路径
3.2.1 设置GT3类路径
选择刚建立的sample工程,点击右键,从弹出窗口中选择属性,打开属性窗口,选择Java build path。选择add External JARs,从文件选择对话框中选择GT3安装目录下lib文件夹(在本文中是D://grid//ogsa//impl//java//lib)中的GT3库文件,都添加进来。如图:
图3 设置类路径
3.2.2 设置SWT和JFace路径
因为本文使用SWT和JFace开发界面,所以需要它们的JAR包。其中swt.jar包在你的Eclipse安装目录//plugin//org.eclipse.swt.win32_2.1.0//ws//win32目录下。Jface.jar包在你的Eclipse安装目录//plugin//org.eclipse.jface.win32_2.1.0目录下。找到这个两个文件并添加到sample工程的类路径中。为了确保Java的虚拟机能够获得在运行时使用正确的SWT GUI的共享库,我们还需要设置一下参数:
-Djava.libarary.path=d://eclipse//plugin//org.eclipse.swt.win32_2.1.0//os//win32//x86, 把相应的JNI本地化库添加进来,这一步在建立运行配置时会用到。下面就可以开发网格服务了。
3.3 开发实现通知功能的EchoString网格服务
OGSI定义了网格服务的通知机制,它将消息的发布方服务接口称为NotificationSource,将消息的接收方服务接口称为NotificationSink。它们之间的传递过程如下图所示:
图4 通知传送的过程
GT3框架对通知机制的实现是通过operation provider实现的。GT3提供了两种方式的网格服务的编程模式:一种是直接继承GridServiceImpl类和其他接口,这些类由GT3提供,并且实现了OGSI定义的接口。另一种方式即operation provider方式,也叫做委托代理的方式。这种方式使得我们可以很容易的在服务部署时"插拔"WSDL文件中所描述的操作的各种不同的实现。目前GT3.0框架以operation provider的方式实现了OGSI所定义的Factory和Notification接口。这样,如果想在自己开发的服务中添加Factory和Notification的行为,只需要在服务的部署描述文件中配置即可。
3.3.1 服务端开发
3.3.1.1 提供服务接口
GT3提供了两种方式用来生成WSDL形式的服务接口:一种是通过Java类接口,运行Java2WSDL工具生成WSDL文档。一种是通过gWSDL文件描述,通过使用GWSDL2WSDL工具来生成WSDL文档。参考资料5《用GT3开发网格服务》中使用得是从Java类接口生成服务接口,服务实现继承GT3提供的GridServiceImpl类的方式。在这里我使用gWSDL文件来描述服务接口,用operation provider的方式实现服务。(详细的资料请参看源文件和GT3编程手册)
1. 定义serviceData模式
在Eclipse中选中sample工程,建立schema//echostring文件夹,这个文件夹用于存放gWSDL文件。
选择新建立的echostring文件夹,右键弹出菜单->New->File,建立echostringstate.xsd文件。如下图:
图5 创建文件
文件内容如下:
echostringstate.xsd<complexType name="EchoStringStatusType"> <sequence> <element name="status" type="string"/> </sequence> </complexType>
GT3通知机制的实现是和ServiceData紧密相关的。要使得通知的消息含有有用的信息,我们要定义一个SDD。当serviceData值改变时,通知源会以如下格式向接受者发送通知消息。这个SDD中,我们定义一个名为status的string变量,用来记录状态。参看源文件中的sample//schema//echostring//echostringstate.xsd。
2.建立gWSDL文件并添加服务数据
用同样的方法在sample//schema//echostring目录下建立NotificationEchoString.gwsdl文件,编辑文件:
sample//schema//echostring//NotificationEchoString.gwsdl…….<gwsdl:portType name="NotificationEchoStringPortType" extends="ogsi:GridService"><!--operations --> <sd:serviceData name="EchoStringStatus" type="echo:EchoStringStatusType" minOccurs="1" maxOccurs="1" mutability="mutable" modifiable="false" nillable="false"> </sd:serviceData></gwsdl:portType>…..
这段代码说明了这个服务有一个类型为EchoStringStatusType的服务数据。
3.实现NotificationSource接口
在gWSDL中添加了服务数据后,为了实现Notification功能还需要在gWSDL中定义了NotificationSource portType:
<gwsdl:portType name="NotificationEchoStringPortType" extends="ogsi:GridService ogsi:NotificationSource"/>
黑体的部分说明它继承了NotificationSource接口,相当于OGSI定义的消息源。
3.3.1.2 生成Stub
1.建立build.xml文件,用Eclipse内嵌的ant进行编译,生成WSDL文件和服务支持文件如stubs。详细请参看sample//build.xml文件。
图6 设置build.xml
然后运行Run->External Tools->External Tools,配置ant。并从Targets中选择generateWSDL,如下图:
图7 配置Ant
然后运行Run,Eclipse就会生成相宜的文件。如下图:
图8 运行Ant
用同样的方法我们可以生成服务的stub文件。详细的过程请参考参考资料3和本文例子中的说明参考资料10。
3.3.1.3 服务实现
生成Stub后,就可以实现服务了。我们用operation provider的方式来实现这个服务。
先建立EchoStringProvide类,如下图:
图9 创建类
然后将生成的build//stubs添加到类路径,这样在编辑EchoStringProvide类时不会报错。如下图:
图10 设置类路径
EchoStringProvider.java的内容如下:
Sample/src/org/nci/sample/echostring/notification/impl/EchoStringProvider.java public class EchoStringProvider implements OperationProvider, GridServiceCallback {……// Operation Provider methods public void initialize(GridServiceBase base) throws GridServiceException { this.base = base; } public QName[] getOperations() { return operations; }public void postCreate(GridContext context) throws GridServiceException { serviceData = base.getServiceDataSet().create("EchoStringState");//创建服务数据 statusData = new EchoStringStatusType(); serviceData.setValue(statusData); statusData.setStatus("initialize"); base.getServiceDataSet().add(serviceData); //添加服务数据 }// EchoString PortType methods public String echoString(String value) throws RemoteException{ System.out.println(" Recieve string "+ value +" from client."); statusData.setStatus(value); serviceData.notifyChange(); return value; }……}
用这种方式实现服务要实现两个接口:OperationProvide和GridServiceCallback。前一个接口要实现两个方法:initialize()和getOperaions()。而在GridServiceCallback接口中的postCreate方法中将添加服务数据中服务中。最后我们要在接口echoString()中调用servicedata的notifyChange函数,声明状态改变。这样每当一个客户调用这些远程方法时,会调用serviceData.notifyChange()操作,向订阅这些通知的接受者发出通知消息。
3.3.1.4 服务配置文件
上述操作完成后,我们需要编写服务配置文件:
sample/echostring-config.wsdd<service name="org/nci/sample/NotificationEchoStringFactoryService" provider="Handler" style="wrapped"><parameter name="name" value="EchoString Notification Factory"/> <parameter name="instance-name" value="EchoString Notification "/> <parameter name="instance-schemaPath" value="schema/echostring/Notification EchoString_service.wsdl"/><parameter name="instance-className" value="org.nci.sample.echostring.notification.NotificationEchoStringPortType"/><parameter name="instance-baseClassName" value="org.globus.ogsa.impl.ogsi.GridService Impl"/><parameter name="instance-operationProviders" value="org.nci.sample.echostring.notification.impl.EchoStringProviderorg.globus.ogsa.impl. ogsi.NotificationSourceProvider"/> ……….</service>
因为我们使用的是operation provider方式,因此参数的instance-baseClassName的值要设为GridServiceImpl,相当于从这个类继承一样,只不过不是在程序中实现,而只是改变一下服务配置文件而已。instance-operationProviders参数的值为我们自己编写的服务实现EchoStringProvider和GT3实现的消息源NotificationSourceProvider。然后编译,打包,并部署到GT3容器上。至此服务端已经开发完成。
3.3.2 客户端开发
OGSI定义通知消息在两个服务之间传递或服务和消息系统之间传递。因此为了能接收通知消息,客户端自己表现的也得像服务一样。GT3自己提供了一个NotificationSinkManager类用来简化这一项工作。NotificationSinkManager的本质是一个服务容器。
首先我们要订阅一个服务实例:
NotificationSinkManager manager = NotificationSinkManager.getManager();manager.startListening(NotificationSinkManager.MAIN_THREAD);String sink = manager.addListener("EchoStringStatus", timeout, source, callback);
在addListener这个方法中会调用服务接口中的subscribe方法,这是自动调用的。然后我们实现回调函数callback,它用来在服务实例和订阅它的客户之间交换ServiceData形式的消息。它必须实现NotificationSinkCallback接口:
sample/src/org/nci/sample/echostring/notification/impl/EchoStringClient.java……public void deliverNotification(ExtensibilityType any) throws RemoteException { ……ServiceDataValuesType serviceData = AnyHelper.getAsServiceDataValues(any);EchoStringStatusType echo=(EchoStringStatusType) AnyHelper.getAsSingleObject (serviceData, EchoStringStatusType.class);System.out.println("Notification Recieve string form service "+echo.getStatus());}
编译,至此客户端开发完成。
下面用一张图来总结一下我们使用Notification的思路,这也是编写一般网格服务的流程:
图11 开发流程图
首先我们感兴趣的是打算在服务和客户之间传送什么样的信息。这些信息是以服务数据的形式在服务中提供的。一旦确定后,我们需要以XML的形式来描述这些信息,也就是所谓的SDD。然后我们需要在WSDL文件中添加这个服务数据,并让服务的portType继承NotificationSource接口。接着在服务实现中需要发送消息的地方调用服务数据的notifyChange方法来通知服务数据发生改变。客户端首先要订阅这个服务,然后在再实现NotificationSinkCallback这个接口中的deliverNotification回调函数,这样服务数据发生改变后会通过deliverNotification来发送消息到订阅的客户。
3.3.3 服务界面开发
SWT的出现使得Java在开发图形界面应用程序上向前迈出了一大步。下面我将使用SWT来编写管理我们开发的echostring服务的GUI程序。目前这个程序的主要功能有:
打算以后对这个GUI程序进行完善功能,使其能够成为一个网格服务管理程序。
3.3.3.1 基本概念
所有的SWT类都用org.eclipse.swt为包的前缀。其中最常用的图形构件都包含在org.eclipse.swt.widgets中。下面对重要的几个构件进行说明。
Display与Shell
Display负责管理SWT和底层操作系统的联系。它最主要的功能是把SWT的事件循环转换为操作系统执行的模式。几乎所有的SWT程序都要求有一个激活的Display。
Shell则代表着桌面上的窗口。Shell在实例化时一般都要求一个Display实例作为其构造函数的参数。SWT中的窗口组成如下图所示,一个窗口由标题栏、菜单、工具栏、客户区和状态栏组成。
图12 窗口的组成
Composite和布局
Composite充当构件容器的角色,它类似与SWING中的Panel对象。如果我们想在窗口中添加一个构件的话,最好先创建一个容器,并选择一个合适的布局方式。最常用的Composite是Group控件,它把几个相似的控件归为一组,可以使得界面变得漂亮整洁。SWT对构件的布局采用Layout和Layout Data结合方式,最常用也是最强大就是GridLayout和GridData。
下面结合具体的程序来说明。服务界面程序实现都在org.nci.sample.echostring.gui包内。
3.3.3.2 创建主窗口
选择sample//src。然后选择File->New->Class,建立MainConsole类。
MainConsole类的主要功能是:
代码如下:
sample//src//org//nci//sample//echostring//gui//MainConsole.java……public Shell open(Display display){ this.display = display; createShell(display);//创建shell createMenuBar();//创建菜单 createSeparator();//创建分隔符 createToolBar();//创建工具条 createSeparator();//创建分隔符 createContent(); //创建客户区 createStatusLine(); //创建状态栏 shell.open(); //打开窗口 return shell; } ……
上述代码清单说的是建立一个Shell,并在此shell上建立菜单、工具条等。结果如图12所示。
3.3.3.3 Menu和MenuItem,TooBar和ToolItem
要在shell上创建菜单,首先需要创建Menu对象,并把它的样式设置成为SWT.BAR的模式。创建了Menu对象后就可以在其上创建MenuItem对象。而MenuItem可以通过setMenu(Menu)方法来设置Menu,这样就可以嵌套菜单项了。如下面的代码清单:
public void createMenuBar(){ Menu bar = new Menu (shell, SWT.BAR); shell.setMenuBar (bar); MenuItem fileItem = new MenuItem (bar, SWT.CASCADE); fileItem.setText ("文件(F)"); fileItem.setMenu (createFileMenu ());//添加文件菜单的菜单项……}
同样我们在shell上创建ToolBar,并创建ToolItem添加到ToolBar中。同时SWT还提供了CToolBar控件,使用这个控件可以使得工具条可以停靠在任意位置。请参看源程序。
3.3.3.4 侦听事件和事件响应
SWT中对用户操作的响应,比如鼠标和键盘事件,也采用SWING和AWT的Observer方式。在org.eclipse.swt.events包中可以找到事件监听的Listener接口和对应的事件。例如几乎所有的控件都有addSelectionListener()接口,并在该接口中的widgetSelected()方法中实现处理函数。如下面代码清单所示,请参考源代码。
…….MenuItem exit = new MenuItem(fileMenu,SWT.PUSH); exit.setText("&退出"); exit.addSelectionListener(new SelectionAdapter(){ public void widgetSelected(SelectionEvent e){ shell.close(); }}); ……
上述代码表示当我们选择菜单中的退出项时,它会调用Shell的close()方法,退出程序。
3.3.3.5 创建客户区
客户区由网格服务容器视图和网格服务视图构成。它们分别是TabFolder中的一个TabItem页面。TabFolder是TabItem的容器,TabItem通过setContorl()方法来设置Tab页面。创建TabFolder的代码如下:
public void createContent(){ tabFolder = new TabFolder(shell,SWT.BORDER); GridData gridData = new GridData(GridData.FILL_BOTH); tabFolder.setLayoutData(gridData); }
通过设置GridData样式,这个TabFolder将充满整个客户区。
由于网格服务容器视图和网格服务实例视图都是TabFolder的一个TabItem,并且它们的界面大同小异,因此它们的实现文件ServiceTab.java和GridServiceTab.java有一个共同的基类Tab.java。Tab.java服务创建这两个TabItem所共有的外观,如树型视图,服务数据组,控制台输出等。下面分别进行说明。
1、网格服务容器视图:
网格服务容器视图见图1。目前它的功能有:
以后会逐渐增加其功能。
列出服务容器服务
GT3容器中的所有服务都在容器注册服务core/registry/ContainerRegistryService中进行注册,通过访问此项服务可以得到这个服务容器中所注册的服务。在本程序中,当调用此功能时,它首先读取位于sample//etc目录的serverConfig.xml文件,得到服务容器的IP和端口。然后在依次访问此项服务。代码如下:
public void getServicesList(String host,String port,String baseLoc,String serviceName){ ……OGSIServiceGridLocator registryService = new OGSIServiceGridLocator(); url = new URL("http://"+host+":"+port+baseLoc+serviceName); GridService registry = registryService.getGridServicePort(url); ExtensibilityType queryResult = registry.findServiceData( QueryHelper.getNamesQuery(ServiceData.ENTRY));ServiceDataValuesType serviceDataValues =(ServiceDataValuesType) AnyHelper.getAsSingleObject( queryResult, ServiceDataValuesType.class ); entries=AnyHelper.getAsObject(serviceDataValues, EntryType.class);……addTreeItem(itemHost,entry.getMemberServiceLocator().getHandle(0).toString());……}
这段代码的首先得到ContainerRegistryService服务实例,然后通过网格服务的标准接口findServiceData()得到此服务的服务数据。然后通过EntryType的getMemberServiceLocator(). getHandle(0)方法得到服务的URL并添加到TreeItem中。请参看源码。
创建服务实例
GT3提供了一个Factory接口来创建网格服务实例。GridServiceFactory实现了这个接口。因此我们首先要创建GridServiceFactory,然后调用createService()方法来创建服务实例。并创建网格服务实例视图。
……OGSIServiceGridLocator factoryService = new OGSIServiceGridLocator();Factory factory = factoryService.getFactoryPort( new HandleType(textL.getText()) ); String id = inputDialog.str; GridServiceFactory gridFactory = new GridServiceFactory(factory); //创建服务 LocatorType locator = gridFactory.createService(null, id);createGridServiceTab(locator);……
其中textL.getText()用于得到服务工厂的URL,id为用户要创建的服务实例名称,它通过一个对话框由用户输入。最后生成服务实例视图。对话框见图13。
图13 输入网格实例名称对话框
2、网格服务实例视图
网格服务实例视图的界面大抵和服务容器视图功能差不多。最大的不同是实例视图添加了如何调用网格服务实例的程序,也就是客户端,网格服务实例视图如下:
图14 网格服务实例视图
实现echostring服务的调用
在GridServiceTab类中添加一个EchoStringClient对象,用来进行客户端调用。在启动服务客户端按钮的响应函数中添加处理函数:
sample//org//nci//sample//echostring//gui//GridServiceTab.java Button clientStart = new Button(clientPanel,SWT.PUSH); clientStart.setText("启动服务客户端"); clientStart.addSelectionListener (new SelectionAdapter () { public void widgetSelected (SelectionEvent e) { startClient(); } }); public void startClient(){ if(client == null){ client = new EchoStringClient(); client.subscribeData(textL.getText(),client); …… } }
至此界面开发完成。
3.4 运行
图15 设置运行参数
4、小结
Eclipse是一款优秀的集成开发环境,它最大的特点是支持插件开发。在本文中,笔者在Eclipse集成了GT3来开发网格服务,利用Eclipse的强大功能,提高了效率。同时Eclipse提供的SWT使用Java开发美观、高效的GUI程序成为可能。在这里笔者结合开发网格服务的管理和使用界面,介绍了一下SWT的功能。文中难免有疏漏之处,欢迎读者多多指教。也欢迎读者多多来信,共同探讨学习。
下一步笔者打算开发一个支持开发网格服务的插件,集成到Eclipse中。
下载本文的 示例代码 及 使用说明 。