LaTeX2html安装及在winEdt界面中的配置
一、下载及安装辅助软件:
1、Perl 编译器(最新版本是5.8.8.817,没有记错的话,CTeX-2.4.5-8 Full 没有安装perl,下载自已安装,安装后的目录是C:\Perl\)
到这里下载:
http://www.activestate.com/Products/Downlo...x?id=ActivePerl
2、NetPBM 图形软件 (CTeX-2.4.5-8 Full 没用安装,下载后自已安装)
到这里下载:
http://tutornet.ru/TEX/Soft/MiKTeX/UTIL/NETPBM/
把下载回来的NETPBM.ZIP解压得到文件夹\NETPBM\netpbm\(包含两个文件夹bin和doc),把netpbm文件夹(包含两个文件夹bin和doc)复制到
C:\CTeX\localtexmf下,最后的目录是C:\CTeX\localtexmf\netpbm\bin\,C:\CTeX\localtexmf\netpbm\doc\
3、Ghostscript (Postscript 解释器,没有记错的话,CTeX-2.4.5-8 Full 已安装好了Ghostscript,在目录C:\CTeX\Ghostgum\下)
到这里下载:
http://www.cs.wisc.edu/~ghost/
小结:安装上述辅助软件后,目录如下:
C:\Perl\
C:\CTeX\localtexmf\netpbm\
C:\CTeX\gs\gs8.51\
C:\CTeX\Ghostgum\
二、设置path环境变量:
将以下路径加到path环境变量:
C:\Perl\bin
C:\CTeX\localtexmf\netpbm\bin
C:\CTeX\gs\gs8.51\bin (CTeX-2.4.5-8 Full 安装完后,这个路径已加到path环境变量中了)
C:\CTeX\localtexmf\l2h\bin (预先加上)
把路径加到path环境变量的方法是:
我的电脑/右击→属性/高级/环境变量→在系统变量选择框中选择path这一行→编辑,在变量值的最后加上
“;C:\CTeX\localtexmf\netpbm\bin;C:\CTeX\localtexmf\l2h\bin”(双引中的内容,不包括双引号,注意不能掉了
最前面的分号)
三、下载及安装主程序latex2html:
1、下载latex2html:
latex2html官方网址: http://www.latex2html.org/
最新版本的latex2html下载地址:http://saftsack.fs.uni-bayreuth.de/~latex2ht/current/latex2html-2002-2-1.tar.gz
2、解压:
将latex2html-2002-2-1.tar.gz解开,得到文件夹latex2html-2002-2-1\latex2html-2002-2-1\,把它重命名为latex2html-2002-2-1\latex2html\,再把其中的文件夹latex2html\复制到C:\LocalTeXMF\下,最后的目录是这样的:C:\CTeX\localtexmf\latex2html\
3、设置目标路径:
具体为--修改 prefs.pm 将'PREFIX'设置如下: $prefs{'PREFIX'} = 'C:\\CTeX\\localtexmf\\l2h';
4、安装latex2html:
⑴、在C:\CTeX\localtexmf\latex2html中双击config.bat。如果在检测 dvips 版本时停住,回几下车。如果程序出错,将反病毒软件暂时禁止。以后运行 LaTeX2HTML 时,可能也需要暂时禁止反病毒软件(一些Perl代码和Norton AntiVirus冲突)。
⑵、然后在C:\CTeX\localtexmf\latex2html中双击 install.bat。一系列的Perl 代码将系统配置好,并自动拷贝到 C:\CTeX\localtexmf\l2h\ 之下。
⑶、最后,在C:\CTeX\localtexmf\latex2html中双击 TEST.BAT 可以测试安装是否成功。 测试一般能成功。
四、编译latex2html自带的例子看看:
在命令行下进入C:\CTeX\localtexmf\latex2html\example文件夹,用下面的命令编译report.tex:
latex2html report.tex
编译完后生成一个report文件夹,打开其中的index.html看看,漂亮吧!!
注意:latex2html编译的源文件(*.tex)不能位于C:\Documents and Settings\这样的路径中,因为这种路径含有空格,而latex2html不允许这样!!
五、在winEdt界面上配置latex2html的编译按钮:
1、把C:\CTeX\localtexmf\latex2html下的latex2html.bat复制到C:\CTeX\WinEdt\Bin\TeX下,把它更名为L2H.bat
2、打开winEdt,在options菜单中单击Menu Setup后打开Menu Setup对话框,在此对话框中,选中Main Menu选项卡,在Items框子中右击&Accessories/Edit,在Menu Items框子中的&HTML的&TtH(TeX→HTML)下面右击/Insert/Utility,打开Main Menu:&Accessories对话框,
在Menu Items框子中,把&L2H (LaTeX->HTML)填入,
在Utility框子中把%B\Bin\TeX\L2H.bat "%N"填入,
在Requires File Filter框子中把%P\%N.tex?%P\%N.ltx填入,
在Start in框子中把%P填入,
在Caption框子中把LaTeX->HTML填入,
在Image and Hint框子把LaTeX->HTML填入,用什么图标自已选。
在Menu Item Options 框子中勾选Save Input File to Disk。
OK,OK关闭这些对话框。
3、把上面这一步Image and Hint的Image选中的按钮放到工具栏上,方法参:http://bbs.ctex.org/forums/index.php?showtopic=31869 第2楼
六、在winEdt界面上配置latex2html预览(preview)按钮:
1、把C:\CTeX\WinEdt\Exec下的HTML Preview.edt复制成L2H Preview.edt,用写字板打开L2H Preview.edt,删除其中的内容,粘贴下面的内容后保存:
// Requires: %P\%N\index.html?%P\%N.chm?%P\%N.php?%P\%N\index.htm?%p\%n.chm?%p\%n.php?%p\%n\index.htm?%p\%n\index.html
// Which file to preview? (change the order if this is not your choice)...
// HTML Help!
IfFileExists("%P\%N.chm",!|HTMLHelp('%P\%N.chm',1,1);Exit;|);
IfFileExists("%p\%n.chm",!|HTMLHelp('%p\%n.chm',1,1);Exit;|);
// To save or not to save?
IfIStr("%F","%P\%N.php",'=',"CMD('Save All');");
IfFileExists("%P\%N.php",!|ShellExecute("Open", "%N.php","","%P",1);Exit;|);
IfIStr("%F","%P\%N.htm",'=',"CMD('Save All');");
IfFileExists("%P\%N\index.htm",!|ShellExecute("Open", "%P\%N\index.htm","","%P",1);Exit;|);
IfIStr("%F","%P\%N.html",'=',"CMD('Save All');");
IfFileExists("%P\%N\index.html",!|ShellExecute("Open", "%P\%N\index.html","","%P",1);Exit;|);
IfIStr("%f","%p\%n.php",'=',"CMD('Save All');");
IfFileExists("%p\%n.php",!|ShellExecute("Open", "%n.php","","%p",1);Exit;|);
IfIStr("%f","%p\%n.htm",'=',"CMD('Save All');");
IfFileExists("%p\%n\index.htm",!|ShellExecute("Open", "%P\%n\index.htm","","%p",1);Exit;|);
IfIStr("%f","%p\%n.html",'=',"CMD('Save All');");
IfFileExists("%p\%n\index.html",!|ShellExecute("Open", "%P\%n\index.html","","%p",1);Exit;|);
End;
2、打开winEdt,在options菜单中单击Menu Setup后打开Menu Setup对话框,在此对话框中,选中Main Menu选项卡,在Items框子中右击&Accessories/Edit,在Menu Items框子中的&HTML的&HTML Preview下面右击/Insert/Macro,打开Main Menu:&Accessories对话框,
在Menu Items框子中,把&L2H Preview填入,
在Macro框子中把"Exe('%b\Exec\L2H Preview.edt');"填入(双号中的内容,不包括双引号),
在Requires File Filter框子中把%P\%N\index.html?%P\%N.chm?%P\%N.php?%P\%N\index.htm?%p\%n.chm?%p\%n.php?%p\%n\index.htm?%p\%n\index.html填入,
在Start in框子中把%P填入,
在Caption框子中把L2H Preview填入,
在Image and Hint框子把L2H Preview填入,用什么图标自已选。
在Menu Item Options 框子中勾选Save Input File to Disk。
OK,OK关闭这些对话框。
3、把上面这一步Image and Hint的Image选中的按钮放到工具栏上,方法参:http://bbs.ctex.org/forums/index.php?showtopic=31869 第2楼。我配置好的菜单和工具栏如图所示:
4、双击打开C:\CTeX\localtexmf\latex2html\example\report.tex,用winEdt工具栏上的按钮进行编译和预览编译好的文件(index.html).爽吧!
一个用OWL-S组装Web服务的例子
OWL-S可以用来描述Web服务,这个帖子将介绍一个非常简单的例子,也许对理解Web服务的组装有些作用。这个服务是对已有Web服务进行组装和执行,所以你并不需要发布自己的Web服务。你需要安装Protege和OWL-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 执行组装后的服务
Jena简介
通过 Jena Semantic Web Framework 在 Jave 应用程序中使用 RDF 模型
级别: 中级 |
开发人员, SmartStream Technologies Ltd
2004 年 7 月
RDF 越来越被认为是表示和处理半结构化数据的一种极好选择。本文中,Web 开发人员 Philip McCarthy 向您展示了如何使用 Jena Semantic Web Toolkit,以便在 Java 应用程序中使用 RDF 数据模型。
“资源描述框架(Resource Description Framework,RDF)”最近成为 W3C 推荐标准,与 XML 和 SOAP 等 Web 标准并排。RDF 可以应用于处理特殊输入数据(如 CRM)的领域,已经广泛用于社会网络和自助出版软件(如 LiveJournal 和 TypePad)。
Java 程序员将越来越多地得益于具有使用 RDF 模型的技能。在本文中,我将带您体验惠普实验室的开放源代码 Jena Semantic Web Framework(请参阅 参考资料)的一些功能。您将了解如何创建和填充 RDF 模型,如何将它们持久存储到数据库中,以及如何使用 RDQL 查询语言以程序方式查询这些模型。最后,我将说明如何使用 Jena 的推理能力从本体推断模型知识。
本文假设您已经就图形、三元组和模式等概念方面对 RDF 比较熟悉,并对 Java 编程有基本的了解。
创建简单的 RDF 模型
我们从基本操作开始:从头创建模型并向其添加 RDF 语句。本节,我将说明如何创建描述一组虚构家庭成员之间关系的模型,如图 1 中所示:
将使用来自“关系”词汇表(请参阅 参考资料)的属性 siblingOf 、 spouseOf 、 parentOf 和 childOf 来描述不同的关系类型。为简单起见,家庭成员用来自虚构名称空间的 URI( http://family/ )进行标识。词汇表 URI 通常以 Jena 代码形式使用,所以将它们声明为 Java 常量会非常有用,减少了错误输入。
Schemagen Schemagen 在命令行中运行,使用的参数包括模式或本体文件的位置、要输出的类的名称和 Java 包。然后可以导出生成的 Java 类,其 还可以使用 Ant 将 Schemagen 作为构建处理的一部分来运行,保持 Java 常量类与正在变化的词汇表保持同步。 |
Jena 的 ModelFactory 类是创建不同类型模型的首选方式。在这种情况下,您想要空的、内存模型,所以要调用的方法是 ModelFactory.createDefaultModel() 。这种方法返回 Model 实例,您将使用它创建表示家庭中每个成员的 Resource 。创建了资源后,可以编写关于这些资源的语句并添加到模型中。
在 Jena 中,语句的主题永远是 Resource ,谓词由 Property 表示,对象是另一个 Resource 或常量值。常量在 Jena 中通过 Literal 类型表示。所有这些类型共享公共接口 RDFNode 。将需要四个不同的 Property 实例表示家庭树中的关系。这些实例使用 Model.createProperty() 创建。
将语句添加到模型中的最简单方法是通过调用 Resource.addProperty() 。此方法以 Resource 作为主题在模型中创建语句。该方法使用两个参数,表示语句谓词的 Property 和语句的对象。 addProperty() 方法被过载:一个过载使用 RDFNode 作为对象,所以可以使用 Resource 或 Literal 。还有有益过载,它们使用由 Java 原语或 String 表示的常量。在示例中,语句的对象是表示其他家庭成员的 Resource 。
通过使用三元组的主题、谓词和对象调用 Model.createStatement() ,还可以直接在模型上创建语句。注意以此种方式创建 Statement 不将其添加到模型中。如果想将其添加到模型中,请使用创建的 Statement 调用 Model.add() ,如清单 1 所示:
|
整个代码示例 FamilyModel.java 还说明了语句批量如何一次添加到模型中,或者作为一个数组或者作为 java.util.List 。
构建了家庭模型后,我们看一下如何使用 Jena 的查询 API 从模型中提取信息。
查询 RDF 模型
程序化地查询 Jena 模型主要通过 list() 方法在 Model 和 Resource 接口中执行。可以使用这些方法获得满足特定条件的主题、对象和 Statement 。它们还返回 java.util.Iterator 的特殊化,其具有返回特定对象类型的其他方法。
我们返回 清单 1的家庭模型,看一下可以查询它的不同方法,如清单 2 所示:
|
最通用的查询方法是 Model.listStatements(Resource s, Property p, RDFNode o) ,下面说明的便利方法都是以其为基础。所有这些参数都可以保留为 null ,在这种情况下,它们作为通配符,与任何数据都匹配。清单 3 中显示了 Model.listStatements() 的一些使用示例:
|
导入和持久化模型
不是所有的应用程序都从空模型开始。更常见的是,在开始时从现有数据填充模型。在这种情况下,使用内存模型的缺点是每次启动应用程序时都要从头重新填充模型。另外,每次关闭应用程序时,对内存模型进行的更改都将丢失。
一种解决方案是使用 Model.write() 序列化模型到文件系统,然后在开始时使用 Model.read() 将其取消序列化。不过,Jena 还提供了持久化模型,它们会被持续而透明地持久存储到后备存储器。Jena 可以在文件系统中或在关系数据库中持久化它的模型。当前支持的数据库引擎是 PostgreSQL、Oracle 和 MySQL。
WordNet WordNet-nouns 模型包含 WordNet 表示的所有“词汇概念”和用于表示每个概念的“单词形式”。例如,它包含由单词形式“domestic dog”、“dog”和“Canis familiaris”表示的词汇概念。 第二个模型是 WordNet-glossary。它提供模型中每个词汇概念的简短定义。“dog”的词汇概念具有词汇条目“a member of the genus Canis (probably descended from the common wolf) that has been domesticated by man since prehistoric times.” WordNet-hyponyms 是第三个模型。它定义模型中概念的层次结构。概念“dog”是概念“canine”下位词,而“canine” 本身是概念“carnivore”的下位词。 |
为了说明如何导入和持久化模型,我将 WordNet 1.6 数据库的 RDF 表示导入到 MySQL 中。因为我使用的 WordNet 表示采用多个单独 RDF 文档的形式,将这些文档导入到一个 Jena 模型中会合并它们的语句。 图 2 说明了 Nouns 和 Glossary 模型合并后 WordNet 模型的片段的结构:
图 2. 合并的 WordNet nouns 和 glossary 模型的结构
创建数据库后台模型的第一步是说明 MySQL 驱动类,并创建 DBConnection 实例。 DBConnection 构造函数使用用户的 ID 和密码登录到数据库。它还使用包含 Jena 使用的 MySQL 数据库名称的数据库 URL 参数,格式为 "jdbc:mysql://localhost/dbname" 。Jena 可以在一个数据库内创建多个模型。 DBConnection 的最后一个参数是数据库类型,对于 MySQL,该参数为 "MySQL" 。
然后 DBConnection 实例可以与 Jena 的 ModelFactory 一起使用来创建数据库后台模型。
创建了模型后,可以从文件系统中读入 WordNet RDF 文档。不同的 Model.read() 方法可以从 Reader 、 InputStream 或 URL 填充模型。可以通过 Notation3 、N-Triples 或默认情况下通过 RDF/XML 语法解析模型。WordNet 作为 RDF/XML 进行序列化,所以不需要指定语法。读取模型时,可以提供基准 URI。基准 URI 用于将模型中的任何相对 URI 转换成绝对 URI。因为 WordNet 文档不包含任何相对 URI,所以此参数可以指定为 null 。
清单 4 显示了将 WordNet RDF/XML 文件导入到 MySQL 持久化模型的完整过程:
|
由于已经填充了 wordnet 模型,以后可以通过调用 ModelMaker.openModel("wordnet",true); 来访问该模型。
仅使用 Jena 的 API 查询像 WordNet 这样巨大的模型将有一定的限制性,因为要执行的每类查询都将需要专门编写多行的代码。幸运的是,Jena 以 RDQL 形式提供了一种表达通用查询的机制。
RDF 数据查询语言(RDQL)
RDQL 是 RDF 的查询语言。虽然 RDQL 还不是正是的标准,但已由 RDF 框架广泛执行。RDQL 允许简明地表达复杂的查询,查询引擎执行访问数据模型的繁重工作。RDQL 的语法表面上类似 SQL 的语法,它的一些概念对已经使用过关系数据库查询的人来说将比较熟悉。在 Jena Web 站点中可以找到极好的 RDQL 指南,但几个简单的示例会对说明基础知识大有帮助。
使用 jena.rdfquery 工具可以在命令行上对 Jena 模型执行 RDQL 查询。RDFQuery 从文本文件中获取 RDQL 查询,然后对指定的模型运行该查询。对数据库后台模型运行查询需要相当多的参数。清单 5 中显示了运行下列示例需要的完整命令行:
|
正如您看到的,这些参数中的大多数参数都提供了创建与 MySQL 的连接所需的详细信息。其中重要的部分是 --query example_query.rdql ,它是 RDQL 文件的位置。还要注意运行 jena.rdfquery 需要 Jena 的 lib 目录中的所有 JAR 文件。
清单 6 显示了您将检查的第一个查询:
清单 6. 查找“domestic dog”的 WordNet 词汇条目的 RDQL 查询
|
SELECT 部分声明查询要输出的变量 — 在本例中,是名为 definition 的变量。 WHERE 子句引入第二个变量 concept 并定义与图形匹配的三元组。查询在具有 WHERE 子句中的所有三元组的图形中查找语句。所以,在英语中, WHERE 子句的意思为“查找具有 'domestic dog' 作为单词形式的概念,并查找这些概念的词汇条目”,如图 3 所示。 USING 子句提供一种便利,用于声明名称空间的前缀。
运行查询的结果为:
|
所以这种情况仅有一个结果。清单 7 中显示的下个查询的意思为“查找单词 'bear' 表示的概念,并查找这些概念的词汇条目”。
清单 7. 查找“bear”的 WordNet 词汇条目的 RDQL 查询
|
此查询返回 15 个结果,因为此单词形式表示多个不同的概念。结果为:
|
清单 8 中显示了另一个示例,查找其他两个单词的上位词(母词):
清单 8. 查找“panther”和“tiger”的 WordNet 上位词的 RDQL 查询
|
此处,查询的意思是“查找单词 'panther' 和 'tiger' 所指的概念;查找第三个概念,前两个概念是其下位词;查找第三个概念的可能的单词和词会条目”,如图 4 所示:
wordform 和 definition 都在 SELECT 子句中声明,所以它们都是输出。尽管词查询仅匹配了一个 WordNet 概念,查询的图形可以以两种方式匹配,因为该概念有两个不同的单词形式:
|
使用 Jena 中的 RDQL
Jena 的 com.hp.hpl.jena.rdql 包包含在 Java 代码中使用 RDQL 所需的所有类和接口。要创建 RDQL 查询,将 RDQL 放入 String 中,并将其传送给 Query 的构造函数。通常直接设置模型用作查询的源,除非在 RDQL 中使用 FROM 子句指定了其他的源。一旦创建了 Query ,可以从它创建 QueryEngine ,然后执行查询。清单 9 中说明了此过程:
|
使用 Query 的一个非常有用的方法是在执行之前将它的一些变量设置为固定值。这种使用模式与 javax.sql.PreparedStatement 的相似。变量通过 ResultBinding 对象与值绑定,执行时该对象会传送给 QueryEngine 。可以将变量与 Jena Resource 或与常量值绑定。在将常量与变量绑定之前,通过调用 Model.createLiteral 将其打包。清单 10 说明了预先绑定方法:
|
QueryEngine.exec() 返回的 QueryResults 对象执行 java.util.Iterator 。 next() 方法返回 ResultBinding 对象。查询中使用的所有变量都可以凭名称通过 ResultBinding 获得,而不管它们是否是 SELECT 子句的一部分。清单 11 显示了如何进行此操作,仍使用 清单 6中的 RDQL 查询:
清单 11. 查找“domestic dog”的 WordNet 词汇条目的 RDQL 查询
|
运行此查询获得的 ResultBinding 如期望的那样包含常量词汇条目。另外,还可以访问变量 concept 。变量通过调用 ResultBinding.get() 凭名称获得。通过此方法返回的所有变量都可以转换成 RDFNode ,如果您想将这些变量绑定回更进一步的 RDQL 查询,这将非常有用。
这种情况下, concept 变量表示 RDF 资源,所以从 ResultBinding.get() 获得的 Object 可以转换成 Resource 。然后可以调用 Resource 的查询方法来进一步探查这部分模型,如清单 12 中所示:
|
程序源码下载中包含的程序 FindHypernym.java(请参阅 参考资料)汇总了您这里研究的区域。它查找命令行上给定单词的上位词,清单 13 中显示了使用的查询:
清单 13. 查找概念的上位词的单词形式和词汇条目的 RDQL 查询
|
命令行上给出的单词与 hyponym 词语绑定,查询查找该单词表示的概念,查找第二个概念(第一个概念是它的下位词),然后输出第二个概念的单词形式和定义。清单 14 显示了输出:
|
使用 OWL 添加意义
您可能想知道为什么“wisteria”的上位词搜索仅返回它的直接上位词“vine”。如果从植物学观点,您可能还希望显示“traceophyte”也显示为上位词,以及“plant”。实际上,WordNet 模型表明“wisteria”是“vine”的下位词,“vine”是“traceophyte”的下位词。直观地,您知道“wisteria”因此是“traceophyte”的下位词,因为您知道“hyponym of”关系是 可传递的。所以您需要有一种方法将这种认识合并到 FindHypernym 程序中,从而产生了 OWL。
可传递关系 可传递关系的一个示例是“大于”关系。如果 a大于 b, b大于 c,因而 a肯定大于 c。 |
Web Ontology Language 或 OWL 是 W3C 推荐标准,设计用来“明确表示词汇表中词语的意义以及那些词语之间的关系”。与 RDF Schema 一起,OWL 提供了一种正式地描述 RDF 模型的机制。除了定义资源可以属于的层次结构类,OWL 还允许表达资源的属性特征。例如,在 清单 1 中使用的 Relationship 词汇表中,可以使用 OWL 说明 childOf 属性与 parentOf 属性相反。另一个示例说明 WordNet 词汇表的 hyponymOf 属性是可传递的。
在 Jena 中,本体被看作一种特殊类型的 RDF 模型 OntModel 。此接口允许程序化地对本地进行操作,使用便利方法创建类、属性限制等等。备选方法将本体看作特殊 RDF 模型,仅添加定义其语义规则的语句。清单 15 中说明了这些技术。注意还可以将本体语句添加到现有数据模型中,或使用 Model.union() 将本体模型与数据模型合并。
|
使用 Jena 推理
给定了本体和模型后,Jena 的推理引擎可以派生模型未明确表达的其他语句。Jena 提供了多个 Reasoner 类型来使用不同类型的本体。因为要将 OWL 本体与 WordNet 模型一起使用,所以需要 OWLReasoner 。
下例显示了如何将 OWL WordNet 本体应用到 WordNet 模型自身以创建推理模型。这里我实际将使用 WordNet 模型的子集,仅包含下位词层次结构中“plant life”之下的那些名词。仅使用子集的原因是推理模型需要保存在内存中,WordNet 模型对于内存模型过大而不能实现。我用来从整个 WordNet 模型中提取 plants 模型的代码包含在文章来源中,名为 ExtractPlants.java(请参阅 参考资料)。
首先我从 ReasonerRegistry 中获得 OWLReasoner 。 ReasonerRegistry.getOWLReasoner() 在它的标准配置中返回 OWL reasoner,这对于此简单情况已经足够。下一步是将 reasoner 与 WordNet 本体绑定。此操作返回可以应用本体规则的 reasoner。然后,将使用绑定的 reasoner 从 WordNet 模型创建 InfModel 。
从原始数据和 OWL 本体创建了推理模型后,它就可以像任何其他 Model 实例一样进行处理。因此,如清单 16 所示,通过 FindHypernym.java 与正常 Jena 模型一起使用的 Java 代码和 RDQL 查询可以重新应用到推理模型,而不进行任何更改:
|
文章来源中有完整清单,名为 FindInferredHypernyms.java。清单 17 显示了当对推理模型查询“wisteria”的上位词时的结果:
清单 17. 运行示例 FindInferredHypernyms 程序
|
OWL 本体中包含的信息已经使 Jena 可以推断“wisteria”在模型中有上位词。
结束语
本文说明了 Jena Semantic Web Toolkit 的一些最重要的功能,并用示例说明了如何创建、导入和持久化 RDF 模型。您已经了解了查询模型的不同方法,并看到了如何使用 RDQL 简明地表达任意查询。另外,您还了解了如何使用 Jena 的推理引擎对基于本体的模型进行推理。
本文中的示例已经说明了将数据表示为 RDF 模型的一些效果,以及 RDQL 从这些模型中提取数据的灵活性。当在您自己的 Java 应用程序中使用 RDF 模型时,这里说明的基本方法将是非常有用的起点。
Jena 是综合的 RDF 工具集,它的功能远不止您这里了解的这些。Jena 项目的主页是开始学习其功能的好地方。
Java与.NET 的Web Services相互调用
本文介绍了Java与.NET开发的Web Services相互调用的技术。本文包括两个部分,第一部分介绍了如何用.NET做客户端调用Java写的Web Services,第二部分介绍了如何用Java做客户端调用.NET开发的Web Services。
二:项目需要的工具
Windows2000 Server(IIS)
Jbuilder9.0( 含有Tomcat , axis)
JDK1.4+Java Web Services Develop
VS.Net 2003
备注:如果没有JBuilder的话就需要自己下载安装Tomcat4.1 以及 Axis的开发包,并做相应配置。由于网上有很多这样的文章,这里就不一一说明了。
三:用.NET做客户端调用Java写的Web Services
1. 生成一个Java Web Services
使用JBuilder 生成一个Web Services是非常简单的,我完全是按照它的帮助做的,只要一步步做下去就可以了。具体路径是Developing Web Services -> tutorials : Web Services(Axis) -> Creating a simple Web Services
2. 发布这个Web Services并得到它的WSDL
Web Services生成好之后,F9运行这个项目。然后,单击View浏览现有的Web 服务,单击Bean1的WSDL连接,我们可以在浏览器中察看它的WSDL描述。在浏览器地址栏复制WSDL地址,为下一步使用.NET开发客户端调用程序做准备。
3. 用VS.NET生成一个.NET的客户端
新建一个项目(WinForm,ASP.net都可以),我在这里使用的是一个Asp.Net项目。在起始页面上放置一个文本输入框用来显示调用Web Services的结果,放置一个按钮,用来单击调用Web Services。然后,选择添加Web 引用,在WSDL一栏中把刚才得到的WSDL地址复制过来,Web 引用的名称输入JavaService,单击添加引用按钮就可以了。此时,我们可以在VS.net 的Solution Explore中看到这个Web 引用。
在按钮的单击事件中输入下列代码:
JavaService.Bean1 bean = new JavaService.Bean1();
TextBox1.Text = bean.getSample.ToString ();
这样,一个.NET客户端就完成了,测试一下,工作正常,OK.
四:用Java做客户端调用.NET写的 Web Services
有了上面的成功,使我以为用Java做客户端调用也是一件十分容易的事情,可实际情况却耗费了我两天时间才得以实现。
1. 用VS.NET新建一个Asp Web Services工程,添加一个web 服务,命名为SumService.asmx。新增一个web method,代码如下:
[WebMethod]
public int IntAdd(int a,int b)
{
return a+b ;
}
然后运行它,并利用IE进行测试成功。
2. 打开jbuilder9.0,新建一个项目,添加一个java class ,命名为TestNetService,输入下列代码:
package MyWebServiceJavaClient;
import java.util.Date;
import java.text.DateFormat;
import java.util.Date;
import java.text.DateFormat;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.namespace.QName;
import java.lang.Integer;
import javax.xml.rpc.ParameterMode;
/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2004</p>
* <p>Company: </p>
* @author not attributable
* @version 1.0
*/
public class TestNetService {
public TestNetService() {
}
public static void main(String[] args) {
try {
Integer i = new Integer(1);
Integer j = new Integer(2);
String endpoint="http://localhost/MyServices/WebServiceTest/SumService.asmx";
Service service = new Service();
Call call = (Call)service.createCall();
call.setTargetEndpointAddress(new java.net.URL(endpoint));
call.setOperationName(new QName("http://www.my.com/SU","IntAdd"));
call.addParameter("a",org.apache.axis.encoding.XMLType.XSD_DATE,javax.xml.rpc.ParameterMode.IN);
call.addParameter("b",org.apache.axis.encoding.XMLType.XSD_DATE,javax.xml.rpc.ParameterMode.IN);
call.setReturnType(org.apache.axis.encoding.XMLType.XSD_INT);
call.setUseSOAPAction(true);
call.setSOAPActionURI("http://www.my.com/Rpc");
Integer k = (Integer)call.invoke(new Object[]{i,j});
System.out.println( "result is " + k.toString() + ".");
}
catch (Exception e) {System.err.println(e.toString());}
}
}
运行上面的java客户端程序,你会发现系统会抛出一个SoapAction异常。奇怪,怎么会错误呢?想到了我开发的asp web services没有指定SoapAction,于是在SumService.asmx中的 [web method] 的上一行添加下列代码:
[SoapRpcMethod(Action="http://www.my.com/Rpc",RequestNamespace="http://www.my.com/SU",ResponseNamespace="http://www.my.com/SU")]
重新编译运行asp web services后,在执行java程序,这时会发现输出了正确的结果3。
这样,用java调用.net生成的web services也算初步完成了,好像也不是很复杂是吧。其实,在实际工作中,我一开始并没有在网上找到一个很好的代码例子,全部是依靠jbuilder 和 axis的帮助完成的, 可总是提示什么找不到相应的SoapAction。而实际上,我在运行了asp web services后是可以在IE浏览器中看到那个SoapAction的。我试验过将默认的SoapAction拷贝到java代码中,但是java客户端依然抛出同样的异常,这要我也很迷惑。
五:总结
经过两天的试验,终于从技术上明确了通过Web Services实现.net 与 java的互通是可能的。当然有几点是需要注意的:
1. 在提供Web Services的时候,尽量使用xml schema中支持的变量类型做参数。如果使用.net 中的dataset这种类型,对于java来说解析起来将是一个灾难,当然,理论上是可以解析的。但是从效率角度来说,在Web Services与客户端交换信息的过程中,始终有一个序列化和反序列化的问题。如果使用dataset这种类型,系统还需要对它进行序列化操作,这将是一个很耗费资源的过程。而使用string类型将简单很多。
2. 如果使用了soap header等扩展功能,例如使用了微软提供的WSE技术,它们之间的相互通信需要作特殊处理。
Creating Web Service Clients with JAX-RPC
This section shows how to create and run these types of clients:
When you run these client examples, they will access the MyHelloService that you deployed in the preceding section.
StaticStubHello is a stand-alone program that calls the sayHello and sayGoodbye methods of MyHelloService. It makes this call through a stub, a local object which acts as a proxy for the remote service. Because this stub is created before runtime (by the IDE), it is called a static stub.
Before it can invoke the remote methods on the stub, StaticStubHello performs these steps:
Stub object named stub:return (Stub)
new MyHelloService_Impl().getMyHelloServiceRPCPort();
The program gets the Stub object by invoking a private method named createProxy. Note that the code in this method is implementation-specific and may not be portable because it relies on the MyHelloService_Impl object. The MyHelloService_Impl class is created by the IDE in when you choose the Generate Client Proxy menu item in Building and Running the StaticStubHello Client.
stub to the service definition interface, MyHelloServiceRPC:MyHelloServiceRPC hello = (MyHelloServiceRPC)stub;
A service definition interface declares the methods that a remote client may invoke on the service. In this example, the interface (MyHelloServiceRPC) defines the sayHello and sayGoodbye methods. The IDE creates the MyHelloServiceRPC class file when you choose the Generate Client Proxy menu item. The IDE gets the name MyHelloServiceRPC from the WSDL file, which was created in Generating the Service's Helper Classes and WSDL File. When the IDE created the WSDL file, it constructed the name of the service definition interface by appending RPC to the service name (MyHelloService).
Here is the full source code listing for the StaticStubHello client:
package staticstub;
import javax.xml.rpc.Stub;
import staticstub.MyStaticGenClient.MyHelloService_Impl;
import staticstub.MyStaticGenClient.MyHelloServiceRPC;
public class StaticStubHello {
public static void main(String[] args) {
try {
Stub stub = createProxy();
MyHelloServiceRPC hello = (MyHelloServiceRPC)stub;
System.out.println(hello.sayHello("Duke"));
System.out.println(hello.sayGoodbye("Jake"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static Stub createProxy() {
// Note: MyHelloService_Impl is implementation-specific.
return (Stub)
(new MyHelloService_Impl().getMyHelloServiceRPCPort());
}
}
These are the basic steps for building and running the client:
The detailed steps follow:
MyHelloService WSDL resides in the helloservice package.In a previous section, Creating MyHelloService, the IDE generated the WSDL file. Later in this section, the IDE reads the WSDL file for information it needs to create runtime classes for the client.
staticstub package and choose File
New
Web Services
Web Service Client.The Web Service Client pane of the New wizard appears.
MyHelloService WSDL of the helloservice package.The MyStatic client node appears in the Explorer.
MyStatic client node and choose Generate Client Proxy.This action creates the MyStatic$Documents and MyStaticGenClient packages. This example will not use the MyStatic$Document package, which contains JSP pages for testing the service.
The MyStaticGenClient package contains the stub class, serializer classes, and other helper classes required by the client at runtime. This package also contains the MyHelloServiceRPC and MyHelloService_Impl classes. Because these classes are referenced in the client's source code, they must be generated before the client is compiled. (See the section StaticStubHello Source Code).
StaticStubHello and choose Execute.The IDE compiles and runs the program. The Output window should display these lines:
Hello Duke
Goodby Jake
In this example, you've run the StaticStubHello client from within the IDE, which can locate the runtime classes with its default classpath. If you were to run the client outside of the IDE, you'd want to create a JAR file containing the runtime classes of the MyStaticGenClient package.
The client in the preceding section used a static stub for the proxy. In contrast, the client example in this section, DynamicProxyHello, calls a remote procedure through a dynamic proxy, an object created at runtime that represents the Web service. Although the source code for the StaticStubHelloClient example relied on an implementation-specific class, but the DynamicProxyHello code does not have this limitation. (However, the DynamicProxyHello client does rely on implementation-specific runtime classes that are generated by the IDE.)
The DynamicProxyHello program constructs the dynamic proxy as follows:
Service object named helloService:Service helloService =
serviceFactory.createService(helloWsdlUrl,
new QName(nameSpaceUri, serviceName));
A Service object is a factory for proxies. To create the Service object (helloService), the program calls the createService method on another type of factory, a ServiceFactory object.
The createService method has two parameters, the URL of the WSDL file and a QName object. In this example, the URL of the WSDL file points to the WSDL that has been deployed with MyHelloService:
http://localhost:80/MyHelloService/MyHelloService?WSDL
A QName object is a tuple that represents an XML qualified name. The tuple is composed of a namespace URI and the local part of the qualified name. In the QName parameter of the createService invocation, the local part is the service name, MyHelloService.
helloService, creates a proxy (myProxy) with a type of the service definition interface (MyHelloServiceRPC):MyHelloServiceRPC myProxy =
(MyHelloServiceRPC) helloService.getPort(
new QName(nameSpaceUri, portName),
MyHelloServiceRPC.class);
The helloService object is a factory for dynamic proxies. To create myProxy, the program calls the getPort method of helloService. This method has two parameters: a QName object that specifies the port name and a java.lang.Class object for the service definition interface. The port name, MyHelloServiceRPCPort, is specified by the WSDL file.
When the IDE creates the WSDL, it constructs the port name by appending RPCPort to the service name (MyHelloService) that you enter in the Specify Web Service pane of the New wizard (See Creating MyHelloService.) The service definition interface, MyHelloServiceRPC, is created by the IDE when you choose the Generate Client Proxy menu item.
The source code for the DynamicProxyHello client follows:
package dynamicproxy;
import java.net.URL;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
import dynamicproxy.MyDynamicGenClient.MyHelloServiceRPC;
public class DynamicProxyHello {
public static void main(String[] args) {
try {
String UrlString =
"http://localhost:80/MyHelloService/MyHelloService?WSDL";
String nameSpaceUri = "urn:MyHelloService/wsdl";
String serviceName = "MyHelloService";
String portName = "MyHelloServiceRPCPort";
URL helloWsdlUrl = new URL(UrlString);
ServiceFactory serviceFactory =
ServiceFactory.newInstance();
Service helloService =
serviceFactory.createService(helloWsdlUrl,
new QName(nameSpaceUri, serviceName));
MyHelloServiceRPC myProxy =
(MyHelloServiceRPC) helloService.getPort(
new QName(nameSpaceUri, portName),
MyHelloServiceRPC.class);
System.out.println(myProxy.sayHello("Buzz"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Before performing the steps in this section, you must first create and deploy the MyHelloService as described in Creating a Web Service with JAX-RPC. The steps for building and running the DynamicProxyHello client are the same as those described in Building and Running the StaticStubHello Client, with the following exceptions:
With the dynamic invocation interface (DII), a client can call a remote procedure even if the signature of the remote procedure or the name of the service are unknown until runtime. In contrast to a static stub or dynamic proxy client, a DII client does not require runtime classes generated by the IDE. However, as you'll see in the following section, the source code for a DII client is more complicated than the code of the other two types of clients.
Note: This example is for advanced users who are familiar with WSDL documents. (See Further Information.)
The DIIHello program performs these steps:
Service object.Service service =
factory.createService(new QName(qnameService));
To get a Service object, the program invokes the createService method of a ServiceFactory object. The parameter of the createService method is a QName object that represents the name of the service, MyHelloService. The WSDL file specifies this name as follows:
Service object, creates a Call object:QName port = new QName(qnamePort);
Call call = service.createCall(port);
A Call object supports the dynamic invocation of the remote procedures of a service. To get a Call object, the program invokes the Service object's createCall method. The parameter of createCall is a QName object that represents the service definition interface, MyHelloServiceRPC. In the WSDL file, the name of this interface is designated by the portType element:
Call object:call.setTargetEndpointAddress(endpoint);
This address is the URL of the service. (For a static stub client, the IDE refers to the endpoint address as the SOAP RPC URL.) In the WSDL file, this address is specified by the element:
location="http://localhost:80/MyHelloService/MyHelloService"/>
SOAPACTION_USE_PROPERTY
SOAPACTION_URI_PROPERTY
ENCODING_STYLE_PROPERTY
To learn more about these properties, refer to the SOAP and WSDL documents listed in Further Information.
QName QNAME_TYPE_STRING = new QName(NS_XSD, "string");
call.setReturnType(QNAME_TYPE_STRING);
call.setOperationName(new QName(BODY_NAMESPACE_VALUE,
"sayHello"));
call.addParameter("String_1", QNAME_TYPE_STRING,
ParameterMode.IN);
To specify the return type, the program invokes the setReturnType method on the Call object. The parameter of setReturnType is a QName object that represents an XML string type.
The program designates the method name by invoking the setOperationName method with a QName object that represents sayHello.
To indicate the method parameter, the program invokes the addParameter method on the Call object. The addParameter method has three arguments: a String for the parameter name (String_1), a QName object for the XML type, and a ParameterMode object to indicate the passing mode of the parameter (IN).
Call object:String[] params = { "Murphy" }; String result = (String)call.invoke(params);The program assigns the parameter value (
Murphy) to aStringarray (params) and then executes theinvokemethod with theStringarray as an argument.Here is the source code for the
DIIHelloclient:
package dii;ult = (String)call.invoke(params);
import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.ParameterMode;
public class DIIHello {
private static String qnameService = "MyHelloService";
private static String qnamePort = "MyHelloServiceRPC";
private static String endpoint =
"http://localhost:80/MyHelloService/MyHelloService";
private static String BODY_NAMESPACE_VALUE =
"urn:MyHelloService/wsdl";
private static String ENCODING_STYLE_PROPERTY =
"javax.xml.rpc.encodingstyle.namespace.uri";
private static String NS_XSD =
"http://www.w3.org/2001/XMLSchema";
private static String URI_ENCODING =
"http://schemas.xmlsoap.org/soap/encoding/";
public static void main(String[] args) {
try {
ServiceFactory factory =
ServiceFactory.newInstance();
Service service =
factory.createService(new QName(qnameService));
QName port = new QName(qnamePort);
Call call = service.createCall(port);
call.setTargetEndpointAddress(endpoint);
call.setProperty(Call.SOAPACTION_USE_PROPERTY,
new Boolean(true));
call.setProperty(Call.SOAPACTION_URI_PROPERTY, "");
call.setProperty(ENCODING_STYLE_PROPERTY,
URI_ENCODING);
QName QNAME_TYPE_STRING = new QName(NS_XSD, "string");
call.setReturnType(QNAME_TYPE_STRING);
call.setOperationName(new QName(BODY_NAMESPACE_VALUE,
"sayHello"));
call.addParameter("String_1", QNAME_TYPE_STRING,
ParameterMode.IN);
String[] params = { "Murphy" };
String resSystem.out.println(result);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Because a DII client does not require generated runtime classes, the procedures for building and running DIIHello are simple.
dii package.DIIHello and choose Execute.The Output window should display this line:
Hello Murphy
教程:如何用Axis2开发和部署Web服务
Axis2 是下一代 Apache Axis。Axis2 虽然由 Axis 1.x 处理程序模型提供支持,但它具有更强的灵活性并可扩展到新的体系结构。Axis2 基于新的体系结构进行了全新编写,而且没有采用 Axis 1.x 的常用代码。支持开发 Axis2 的动力是探寻模块化更强、灵活性更高和更有效的体系结构,这种体系结构可以很容易地插入到其他相关 Web 服务标准和协议(如 WS-Security、WS-ReliableMessaging 等)的实现中。
Axis2 的特性包括:
本系列文章以 Axis2 1.0 版本为基础。您可以在 Apache 网站获得 Axis2 的最新版本。
|
Axis2 体系结构将逻辑与状态分离;这允许在并行线程中执行逻辑。服务和调用的静态状态和动态状态分别存储在 Description 和 Context 类中。Axis2 体系结构是使用 7 个独立模块实现的。
Description 类存放本质上是静态的且存在于 Axis 引擎实例的整个生命周期中的数据(如传输、服务和操作的配置)。Context 类存放调用上下文中有效的服务和操作的动态信息,例如当前请求和响应 SOAP 消息、From 地址、To 地址和其他元素。
|
部署 Axis2 与部署 Axis 1 一样简单。首先在 Axis2 二进制代码分发包的 webapps 目录下查找 Axis2 Web 应用程序 axis2.war。在 servlet 容器中部署此 war 文件。在 Tomcat 中,如果已在服务器配置中将 unpackWARs 设置为 True,则只需将 axis2.war 复制到 $TOMCAT_HOME/webapps 目录即可部署 Axis2。请立即启动 Tomcat 并访问 http://localhost:<port>/axis2。将显示 Axis2 欢迎页,单击此页上的 Validate 链接。您应到达 Axis2 Happiness page,不会出现任何错误。
|
下面介绍如何使用 In-Only subscribe() 和 In-Out getQuote() 这两个操作来开发 StockQuoteService。subscribe() 操作将预订指定代号的每小时报价,而 getQuote() 将获得指定代号的当前报价。
下面的清单 1 是 StockQuoteService 的实现示例:
|
Axis2中的om子包在V1.0版本中从此前的axis2包移至axiom包中。
|
部署描述符
在 Axis2 中,服务部署信息包含在 services.xml 文件(在 0.92 以前的版本中,此文件名为 service.xml)中。对于上述 StockQuoteService,服务部署描述符与下面的清单 2 类似。
|
服务的 name 属性定义服务的名称。Axis2 使用服务的名称创建服务的端点地址,例如 http://localhost:<port>/axis2/services/<nameofservice>。因此,对于 StockQuoteService,服务端点为 http://localhost:<port>/axis2/services/StockQuoteService。ServiceClass 参数指定服务实现类。
每个 <operation> 元素定义服务中一个操作的配置。<operation> 的 name 属性应设置为服务实现类中方法的名称。messageReceiver 元素定义用于处理此操作的消息接收器。Axis2 针对 In-Only 和 In-Out 操作提供了两个无数据绑定的内置 MessageReceivers;org.apache.axiom.receivers.RawXMLINOnlyMessageReceiver 用于 In-Only 操作,而 org.apache.axiom.receivers.RawXMLINOutMessageReceiver 用于 In-Out 操作。如果没有指定 messageReceiver,则 Axis2 将尝试使用 org.apache.axiom.receivers.RawXMLINOutMessageReceiver 作为缺省的 messageReceiver。上述 RAWXML 消息接收器将传入 SOAP 消息的 <Body> 的内容作为 OMElement(OMElement 是 XML 元素的 AXIOM 缩写)传递给服务实现。此操作应作为 OMElement 返回 SOAP 响应的 <Body> 元素包含的 XML 内容。这便解释了为何 subscribe() 和 getQuote() 操作采用和返回 OMElement。
services.xml 还可以包含分为 servicegroup 的多个服务。
打包
Axis 2 服务是作为 Axis Archive (.aar) 打包的。这是一个 JAR 文件(使用 jar 或 zip 实用程序创建),在存档的 META-INF 目录中打包了 services.xml 文件。StockQuoteService 在打包成 StockQuoteService.aar 时将具有以下结构:
|
预先打包的 StockQuoteService 存档可以在本文的下载部分中找到。
因为StockQuoteService没有采用RPC方式的消息接收器,用StockQuoteService?wsdl方式不能自动生成wsdl文件,解决办法是手工或用其它工具生成相应的wsdl文件,将其打包在StockQuoteService.aar中。此时StockQuoteService打包成 StockQuoteService.aar 将具有以下结构:
|
部署
在 Axis2 中部署服务相当简单,只需将 .aar 文件复制到 servlet 容器的 axis2 Web 应用程序中的 axis2/WEB-INF/services 目录下即可。对于 Tomcat,此位置为 $TOMCAT_HOME/webapps/axis2/WEB-INF/services。
另一种部署服务的好方法是使用 Axis2 管理控制台中的 Upload Service 工具。请转到 http://localhost:<port>/axis2,然后选择 Administration 链接。输入用户名和密码 admin/axis2,然后登录。(您可以在 axis2.xml 中配置用户名/密码。)在工具部分选择 Upload Service 链接,再选择 .aar 文件,然后单击 Upload。就是这样简单!如果上传成功,系统将显示一条绿色成功消息。服务即被部署,而且可随时调用。如果要在远程 Axis2 服务器上部署服务,则此功能非常方便。
|
Web 服务调用的特性由 MEP、传输协议以及客户端 API 的同步和/或异步行为决定。Axis2 当前支持 WSDL 2.0 定义的 In-Only 和 In-Out MEP。Axis2 客户端 API 支持服务的同步和异步调用。在调用 In-Out 操作时,在 API 级别和传输级别提供异步行为。API 级别异步是通过回滚获得的,它使用一个传输连接来同时传输请求和响应(例如,通过一个 HTTP 连接传输请求和响应)。在传输级别异步中,使用不同的传输连接分别发送请求和接收响应,例如使用 SMTP 进行传输时即如此。
下面是使用 Axis2 客户端 API 调用 In-Only 和 In-Out 操作的详细信息。
org.apache.axis2.clientapi.MessageSender 类用于调用 In-Only 操作(如下面的清单 3 所示),而 In-Only 操作调用 StockQuoteService 的 subscribe() 操作。
|