一个用OWL-S组装Web服务的例子

OWL-S可以用来描述Web服务,这个帖子将介绍一个非常简单的例子,也许对理解Web服务的组装有些作用。这个服务是对已有Web服务进行组装和执行,所以你并不需要发布自己的Web服务。你需要安装ProtegeOWL-S Editor插件,我用的版本前者是3.1 beta build 191,后者是build 15,它们在一起运行得还不错。

所用的Web服务在http://www.bs-byg.dk/hashclass.wsdl,它包含两个方法:HashString和CheckHash,前者用指定编码方式(MD5、SHA1等等)对指定字符串编码,后者根据指定编码方式检查一个字符串(HashString)是否是另一个字符串(OriginString)的编码结果。我们将把这两个方法组装成一个服务,对输入的编码方式和待编码字符串先进行编码,然后检查编码的结果是否正确,如果正确返回true,否则返回false。下面是组装步骤,完整的工程在这里下载

1、确认你的OWL-S Editor已经安装到Protege里,启动Protege,新建一个owl文件类型的工程,在菜单project->config里勾选上owls选项,按确定后Protege的主界面会多出一个OWL-S Editor页。

2、转到OWL-S Editor页,按左上角的WSDL按钮,在弹出的对话框里输入Web服务的地址http://www.bs-byg.dk/hashclass.wsdl,然后按回车,过一会儿在对话框里会显示出这个Web服务的信息,左边是Operations列表。

图1 用来导入WSDL的对话框

3、因为每次只能import一个Operation,所以先选择HashString,然后按右下方的Import按钮,这时系统会提示要把生成的owls文件(扩展名为.owl)保存在一个位置,你可以选择任何位置。

4、使用同样的方法把CheckHash方法也导入进来,这样我们就有了两个可用于组装的Web服务了。如果你愿意的话,可以单独执行看看,方法是选择一个Service,然后按绿色的执行按钮。

图2 导入的两个服务

5、现在开始组装它们。为此我们新建一个Service实例(按Create Service按钮)、一个Profile实例、一个CompositeProcess实例和一个WSDLGrounding实例,分别命名为myservice、myprofile、myprocess和mygrounding好了。

6、接下来把它们连接起来,首先选中myservice,把它的describedBy属性置为myprocess,presents属性置为myprofile,supports属性置为mygrounding。

7、现在对myprocess进行编辑,OWL-S Editor提供了一个可视化的编辑界面(Visual Editor),利用它可以很方便的定义CompositeProcess的各个步骤。选中myprocess,右边切换到Visual Editor,这里有一些粉红色的按钮用来定制流程。我们首先创建一个Sequence(表示按顺序执行),然后选中这个Sequence,创建两个Perform和一个Produce,每个Perform代表调用一个Web服务,而Produce的作用是在最后得到返回值。这时右边的图形应该像下面这样,因为我们尚未对Perform和Produce进行定制。

图3 包含三个有用节点的process图

8、在图形的Perform/Produce节点上点一下就可以修改它的属性,先来修改第一个。点一下第一个矩形节点(第一个Perform),在对话框里把process属性设置为wi1:HashStringProcess(注意:如果导入WSDL时改变了前缀,这里就不是wi1),为了方便阅读,把Name属性改为hashPerform。类似的,第二个矩形节点的process属性应该是wi2:CheckHashProcess,Name则改为checkPerform;对于唯一的Produce节点,改名为produce。现在右边的图如下所示。

图4 改名后的process图

9、现在从Visual Editor切换到Properties页,在这里为myprocess定义输入和输出参数。它的输入应该是wi1:HashType和wi1:Str,而输出应该是wi2:CheckHashResult,也就是说,对于我们组装出来的Web服务来说,输入是编码类型和待编码字符串,而输出是验证结果。

10、上面我们定义了myprocess拥有的参数,现在就要用到它们了。切换回Visual Editor,在树型列表里选则第一个Perform(hashPerform),把右边切换到Properties页,现在ToParameter属性里还是空白,我们要把myprocess的输入映射到这个Perform,所以按添加按钮把两个输入参数(wi1:HashType和wi1:  Str)加到ToParameter里。选中它们中的一个,可以看到右边有BindingType选项,缺省为valueSource这一项,就用它即可,在下面的FromPerform下拉框里只有一个选项TheParentPerform,选中它。在最下面的FromParameter里选择和你选择的ToParameter项一样的那个选项(wi1:HashType->wi1:HashType,wi1:Str->wi1:Str)。

图5 通过参数传递产生“数据流”

11、对于checkPerform,它有三个输入参数,我们希望HashType和hashPerform具有同样的值,所以它的设置和上一步里对HashType的设置一样;OriginalString的设置和上一步的Str一样;HashString属性是上一步得到的结果,所以FromPerform属性应该是hashPerform,FromParameter属性则是wi1:HashStringResult。

12、对produce的设置很简单,在ToParameter属性里加入我们要的结果wi2:CheckHashResult,FromPerform选checkPerform,FromParameter选wi2:CheckHashResult。现在,myprocess对应的process图如下所示。

图6 可以从图中看到服务的结构

13、对myprocess的设置到此就结束了,最困难的部分完成了,剩下的工作都很简单和显而易见。选中mygrounding,在它的hasAtomicProcessGrounding属性里加上wi1:HashStringAtomicProcessGrounding和wi2:CheckHashAtomicProcessGrounding。

14、现在myservice已经可以执行了(myprofile里还可以增加一些信息用来描述这个服务)。现在选中myservice,按下执行按钮,在弹出的对话框里HashType框填MD5,Str框填test(任意字符串都可以),然后按Execute按钮就会看到结果,当然,这个服务不论你输入什么字符串都会得到true值,原因不用我说了吧。

图7 执行组装后的服务

由模型生成GEF应用程序:Merlin

接触和使用过EMF的朋友都知道,只要定义好ecore模型,就能够利用EMF的代码生成工具得到一个可用编辑器,而ecore模型可以从rose模型、java接口或xml schema很方便的转换生成。不过,虽然得到的编辑器从功能上来说是足够了,但针对不同的需求还须要进行定制,这里的工作并不少,如果想定制为图形化编辑器要改的就更多了,基本上相当于重写的工作量。

Merlin开源项目可以为我们节约不少的时间,它是一个基于EMF的模型转换和代码生成工具。EMF本身具有生成代码的功能(例如生成模型java代码、编辑框架和编辑器),Merlin是对它的增强,支持生成GEF实现的图形化编辑器。在介绍EMF的各种资料里经常可以看到图书馆的例子,它的rose模型可以通过这个链接下载到,这里就简单介绍一下利用这个模型生成GEF应用程序的过程。我用的环境是Eclipse 3.1M7、GEF 3.1M7和EMF 2.1.0(注意:EMF用3月份版本会报错,升级为5月26的版本正常)。

首先新建一个EMF项目,名称叫library就可以,选择rose模型导入方式,然后输入URL或本地文件名(library.mdl)就可以得到library.ecore和library.genmodel文件,其中前者是ecore模型(元模型为ecore),后者是与其同步的供EMF代码生成器使用的一个模型。需要的话,可以利用缺省编辑器对.genmodel文件里的各个节点进行一些设置,如包名、属性的性质等等。

然后利用genmodel编辑器从library.genmodel生成模型代码、编辑框架和编辑器,这样我们就得到了三个完整的项目(library、library.edit和library.editor),后面将要生成的GEF编辑器会依赖前两个。

现在利用新建向导创建一个GEF Generation Model(在Merlin Tools->GEF类别下),名字取为library.gefgenmodel,输入文件就是上一步得到的library.genmodel文件,向导结束后即可得到用来生成GEF编辑器的模型文件。在这个文件里可以对将要生成的程序进行初步设置,例如各种类名、包名、Palette里的工具项、EditPart上都安装哪些EditPolicy以及对应的图形等等。

最后,在.gefgenmodel文件编辑器里选中Library:Gen GEF Model节点,点鼠标右键激活上下文菜单,执行里面的“Generate”命令即可生成新的library.gef项目,这个项目包含一个完整GEF应用程序应该具有的各部分代码,但其中一些类是与merlin相关的,例如生成的LibraryEditor继承自com.metys.merlin.generation.gef.editor.GEFEditor,LibraryEditPart也是如此,我觉得如果是完全独立的就更好了,否则还要对Merlin有所了解……

一般的步骤就是上面这样,如果有更详细的修改就要在得到的代码里进行了,和修改EMF生成的编辑器是一样的,修改后不要忘记去掉前面的“generated”标记。其实如果不对.genmodel和.gefgenmodel做任何修改,全部使用缺省设置也能够得到可以工作的代码,只是没有“个性”而已,对图书馆模型的这种“标准”实现如下图。

图1 用Merlin生成的图书馆编辑器,基本功能都有了

生成GEF编辑器只是Merlin的一个小功能而已,Merlin的作者一定是Jet高手,并且对模型映射有所研究,而后者在MDA中具有重要作用。从Merlin的包名来看,它以前大概是某公司的产品,后来转为开源项目继续开发的,虽然目前开发者名单里只有两个成员,但版本的发布却十分频繁,在我看来质量也不错,对于使用EMF的开发者来说应该是个有用的工具。

BTW,Eclipse.org上新增了一篇教你如何结合EMF与GEF的文章:Using GEF With EMF,我就是从这里面发现Merlin的。

关于本体编程的实现

临近期末,我有一门课程的期末项目是做一个教育领域的本体应用系统,所以最近经常思考本体在这样一个系统中所起的作用,以及该如何实现。(本体是否只能在web环境下发挥作用,使用本体描述一个独立系统的模型是否值得?)

假设要做的是选课系统,很容易看出系统里应该有这些对象:课程、学生、教师,它们之间互有联系。现在的问题是,本体、Java类和数据库各扮演怎样的角色?我目前想到的方法有以下几个:

  1. 在本体(owl)里建立这些类和关系,在Java里建立同样的Bean类,运行时系统把本体里的individuals转换为Java类的实例,也就是在内存里得到一个Java实例的集合,选课的各种操作就是对这个集合的修改,退出系统时再进行反向转换,把修改反映到本体里。在这样的实现方法中,本体实际上扮演了数据库的角色,所以当数据(individuals)很多的时候,效率会很成问题。(Protege的owl编辑工具提供了从本体生成EMF模型代码的功能,虽然目前还是alpha版,这也许将成为同步两种模型的不错选择。)
  2. 对上面的方法进行修改,让本体不保存individuals,对Java实例集合的修改最终反映到关系数据库里(利用hibernate不会很困难),运行时系统直接从数据库里取得数据转换为Java实例。这样的实现可以解决效率问题,但和传统应用区别不大,本体的作用几乎为零。
  3. 另一种比较极端的做法是只用本体维护类和individuals,Java方面没有任何与应用有关的模型,系统一开始把本体读入内存以rdf图的方式存在,选课操作转换为对此图的修改(利用Jena等API)。这个方法存在两个问题,一是效率,二是Java代码的可读性下降,这就相当于直接使用JDBC而不是hibernate对数据库操作的区别。

在solo项目里我使用的是第一种方式,各方面效果还可以接受,但本体的作用发挥得很不够。这是很重要的方面,因为如果和传统项目没有区别,辛辛苦苦引入本体又是为了什么,特别是对本体推理的功能,我想最关键的问题还是要找出最适合应用的本体,定义本体的确是一门学问。

Update:IBM Alphaworks也提供了一组本体工具(包括Orient、EODM和RStar),对EMF的支持应该不错。今天初步试了一下Orient,它只支持RDF(S),而不支持OWL,所以无法满足课程项目的要求,Orient的目前版本还有一些小bug,除已知的那些以外,我把.ontology文件输出为ecore模型总是不成功,而输出为rdf是可以的。

Update:推荐一个关于本体和模型驱动的幻灯片,主要内容是介绍应该如何利用UML的可视化编辑功能和元模型的扩展功能来构造本体,这里面介绍了相当多的相关概念(其中很多我甚至没听说过),以及它们出现的原因,比较有利于我们理清思路。