aspire是一个rad工具,提供使用者以标准的xml/xsl、j2ee及关联式资料库技术快速地创建网站。aspire可接受的资料来源包含存储程序、sql语句、ejbs及java控件,并使用apache 的xerces/xalan作为转换的处理器。
aspire可使用于多种servlet引擎,例如apache/jserv、apache/tomcat及websphere等。
目前发展中的功能包含crystal reports及oracle reports等报表工具的整合界面。 aspire优点如下:
1.降低开发时间,开发者只需专注在商业逻辑及gui的样子
2. 降低开发技术门槛
3. 更佳的gui界面
4.支援标准的技术
5. 具有延展性及弹性
一.何为层次数据集?为什么要用层次数据集?
层次数据集并不是一个新的名词。他们已经以客户信息控制系统的事务数据、目录文件、java对象、更接近于xml文件的格式存在了。早在2001年的xml杂志上,我就认为程序员可以从层次数据集的提取上得到好处,尽管他们的数据源具有很强的数据相关性(像:mysql, oracle, sql server, db2等等之类的数据库)。.net思想和数据集概念也很相似。尽管我所认为的层次数据集和传统的微软的数据集有很大的不同,层次数据集具有的丰富的细节描述极大增强了关系型数据的提取。
本文重在讲述层次数据集的结构和与其相关的java api。并不像xml杂志两年前所说的,你现在就可以用一系列的可执行代码来获取层次数据集的优势。当然我们可以在java里编写一定代码来实现对各种各样的数据源的访问以及创建一个完整的层次数据集,本篇中我们就将给出一可执行的代码,你可以利用这些代码并结合一个简单的预定义的关系型适配器就可以创建一些层次数据集啦。这里的关系型适配器包括:文本阅读器,sql 脚本, 存储过程等等。
现在你可能会问了:“这个层次数据集究竟有什么好啊?”。当然层次数据集是不能和你的那些受到奖赏的同事得到的昂贵的carbon带来的实惠相抗衡,在编程中层次数据集是非常有用的。对一个启动来说,一个层次数据集就可以满足整个html页面上的所有的有价值的数据的需要。在mvc(model-view-controler)模型里,控制器的servlet可以提交一个层次数据集给jsp页面后,jsp页面就可以显示它而不需要任何的帮助。也可以说,通过控制器的servlet可直接将其转换为xml格式返回给请求者。从实用的角度来说,数据集可以转换成excel格式的文件。从新潮的角度来说,层次数据集可以转换为支持xml数据的报表或者图表。
本文主要焦点是服务于java程序员的java编程的api怎么运用层次数据集,实际上非java程序员也可以通过有效地结合层次数据集和一个j2ee的服务(比如tomcat)从关系型数据库或者别的数据库中得到xml、html、或者excel格式文档。不要过于急躁,我们先来研究研究层次数据集的结构和这些数据集是如何被提取的。
二. 层次数据集的结构
层次数据集可以表示为一个javaapi ,xml或者别的格式,而用xml来表示将会更加形象:
| <aspiredataset> <!-- a set of key value pairs at the root level --> <key1>val1</key1> <key2>val2</key2> <!-- a set of named loops --> <loop name="loop"> </loop> <loop name="loop2"> </loop> </aspiredataset> |
这是一系列的key/value对.一个给定的key/value可以用在n个独立的loops当中.其实每一个loop 就是一个数据表.loop可以说是table的同义词了.我没有用table这个术语是为了防止人们会不由自主的联想到关系型数据表.已经说过了loop其实上是很多行记录的集合,现在让我们在认真的看loop的结构:
| <loop name="loopname"> <row> <!-- a set of key value pairs --> <key1>val1</key1> <key2>val2</key2> <!-- a set of named loops --> <loop name="loopname1"> </loop> <!-- a set of named loops --> <loop name="loopname2"> </loop> </row> <row> </row> </loop> |
这里唯一一个不成对的结构就是row结构了.一个row结构可以是一些key/value对的集合.这里的row不仅包含了一些key/value对,并且还包括了多个独立的loop结构的递归集.这种扩展可以生成一定深度的树结构.
三.java当中的层次数据的结构
当我把层次数据集以xml的形式展示的时候,你可能会把层次数据集理解为字面上的xml,因此你会先到dom,接着你甚至会想这样岂不是会占用很大的jvm内存.不必慌张.层次数据集有自己的的java api二不需要dom来描述.下面就是一个层次数据集的java api代码:
| package com.ai.htmlgen; import com.ai.data.*; /** * represents a hierarchical data set. * an hds is a collection of rows. * you can step through the rows using iloopforwarditerator * you can find out about the columns via imetadata. * an hds is also a collection loops originated using the current row. */ public interface ihds extends iloopforwarditerator { /** * returns the parent if available * returns null if there is no parent */ public ihds getparent() throws dataexception; /** * for the current row return a set of * child loop names. iloopforwarditeraor determines * what the current row is. * * @see iloopforwarditerator */ public iiterator getchildnames() throws dataexception; /** * given a child name return the child java object * represented by ihds again */ public ihds getchild(string childname) throws dataexception; /** * returns a column that is similar to sum, avg etc of a * set of rows that are children to this row. */ public string getaggregatevalue(string keyname) throws dataexception; /** * returns the column names of this loop or table. * @see imetadata */ public imetadata getmetadata() throws dataexception; /** * releases any resources that may be held by this loop of data * or table. */ public void close() throws dataexception; } |
简单的说来,上面的ihds接口就是一个层次数据集的接口.这个api使你可以递归的访问你的loop结构.这个接口里有一些遍历loop结构是需要的一些选项.它也能假定是前序遍历或者随机遍历.现在我来介绍的是这个api用到的两个附加的接口: iloopforwarditerator和imetadata:
如何在ihds里遍历行记录的接口: iloopforwarditerator
| package com.ai.htmlgen; import com.ai.data.*; public interface iloopforwarditerator { /** * getvalue from the current row matching the key */ public string getvalue(final string key); public void movetofirst() throws dataexception; public void movetonext() throws dataexception; public boolean isattheend() throws dataexception; } |
imetadata: 用于读取列名的接口
| package com.ai.data; public interface imetadata { public iiterator getiterator(); public int getcolumncount(); public int getindex(final string attributename) throws fieldnamenotfoundexception; } |
怎么得到层次数据集以及怎么使用?
现在我们已经知道了层次数据集的结构了,你又怎么去利用它呢?像我以前所说的,这些再aspire下是非常容易的.具体的步骤如下:
1. 学习aspire的基础知识
2. 为你的层次数据集创建定义文件
3. 在java 代码里调用你的定义和ihds接口
下面具体介绍了这里面的细节:
阅读aspire jar的基础的使用方法:
aspire是一个很小的jar文件,当你用像tomcat这样的app服务器的时候,它是你的java程序的一个补充.再aspire的核心是一系列的配置文件,在这些文件里你可以声明你的根据java类的数据访问机制和这些java类的评论.aspire将执行这些java类并返回期待的结果对象.层次数据集是没有异常的.
一个早期的标志性的对aspire的评论参见:“ for tomcat developers, aspire comes in a jar”.配置了初始化一个aspire就像你定义数据库.调用sql语句或者存储过程一样.
为你的层次数据集创建定义文件:
一个层次数据集的定义实例:
| ################################### # ihdstest data definition: section1 ################################### request.ihdstest.classname=com.ai.htmlgen.dbhashtableformhandler1 request.ihdstest.loopnames=works #section2 request.ihdstest.works.class_request.classname=com.ai.htmlgen.generictablehandler6 request.ihdstest.works.loopnames=childloop1 request.ihdstest.works.query_request.classname=com.ai.data.rowfilereader request.ihdstest.works.query_request.filename=aspire://samples //pop-table-tags//properties//pop-table.data #section3 request.childloop1.class_request.classname=com.ai.htmlgen.generictablehandler6 request.childloop1.query_request.classname=com.ai.data.rowfilereader request.childloop1.query_request.filename=aspire://samples//pop-table-tags //properties//pop-table.data |
这个定义包括三个部分. 这个数据集的名字叫ihdstest.第一部分告诉aspire:java类com.ai.htmlgen.dbhashtableformhandler1负责返回一个对象实现ihds.如果你不自己编写你自己的ihds实现,在你定义每一个数据集的时候都必须用到这个类.这个预定义的类知道如何把关系型的数据组成以个层次型的数据集.第一部分的第二行告诉了dbhashtableformhandler1含有一个循环的主数据集是works.第二部分定义了循环works.在aspire里一个循环结构用到了两个java类:一个叫请求类(generictablehandler6) 另一个是查询请求类(rowfilereader). rowfilereader从一个一维文件当中读取一系列的记录并把他们转换成一些行和列严格定义的集合. generictablehandler6根据这个集合结合调用像汇总数值和行数之类的功能在循环里事项接口ihds. generictablehandler6像dbhashtableformhandler1一样在很多的定义当中都将被调用. rowfilereader将会根据你所使用的数据源的不同将会做稍微的调整.下面的例子里就列出了这些情况:
1. rowfilereader.
2. dbrequestexecutor2 (for reading sql).
3. storedprocedureexecutor2 (for reading from stored procedures).
4. xmlreader (for reading xml files).
5. or, you can write your own reader that implements idatacollection.
第二部分说明它有一个子类childloop1. 在generictablehandler6里将会暗示我们到第三部分去找这个子类childloop1.在第三部分定义了childloop1.这个定义是和第二部分里的一样的,除非它没有子类childloop1.第一和第二部分都用到了rowfilereaders.实际上它可以用于几乎所有的数据读取部分.
现在我们来调用文件ihds-test.properties.下面是把这个文件包括到aspire的主文件aspire.properties的例子:
| application.includefiles=aspire://samples//hello-world //properties//hello-world.properties,/ aspire://samples//ihds-test//ihds-test.properties,/ aspire://samples//xml-reader//xml-reader.properties |
为了完整性.我用了双斜线在处理当中.
调用你的定义并得到一个ihds.
现在我们已经又了层次数据集的定义,我们如何从java里调用它呢?认真阅读第一篇文章对你将会有很大帮助的,下面是相应的java代码:
| hashtable args = new hashtable(); args.put("key1".tolowercase(), "value1"); ifactory factory = appobjects.getfactory(); ihds hds = (ihds)factory.getobject("ihdstest",args); // use ihds |
aspire有一个ifactory接口描述的factory服务.你可以通过这个factory接口调用java类ihdstest,并把所有的参数以哈希表的格式传入.小写的字符串对于一个关系型的适配器的下载流是很必要的.
下面的代码将遍历ihds树,并把它输出到屏幕:
| import com.ai.htmlgen.*; import com.ai.common.transformexception; import java.io.*; import com.ai.data.*; // above code removed for clarity public static void statictransform(ihds data, printwriter out) throws transformexception { try { writealoop("maindata",data,out,""); } catch(dataexception x) { throw new transformexception( "error: debugtexttransform: data exception",x); } } /********************************************************** * a recursive function to write out a loop worth of ihds **********************************************************/ private static void writealoop( string loopname, ihds data, printwriter out, string is) throws dataexception { println(out,is, ">> writing data for loop:" + loopname); // write metadata imetadata m = data.getmetadata(); iiterator columns = m.getiterator(); stringbuffer colbuffer = new stringbuffer(); for(columns.movetofirst();!columns.isattheend();columns.movetonext()) { string columnname = (string)columns.getcurrentelement(); colbuffer.append(columnname).append("|"); } println(out,is,colbuffer.tostring()); //write individual rows for(data.movetofirst();!data.isattheend();data.movetonext()) { stringbuffer rowbuffer = new stringbuffer(); for(columns.movetofirst();!columns.isattheend();columns.movetonext()) { string columnname = (string)columns.getcurrentelement(); rowbuffer.append(data.getvalue(columnname)); rowbuffer.append("|"); } println(out,is,rowbuffer.tostring()); // recursive call to print children iiterator children = data.getchildnames(); for(children.movetofirst();!children.isattheend();children.movetonext()) { // for each child string childname = (string)children.getcurrentelement(); ihds child = data.getchild(childname); writealoop(childname,child,out,is + "/t"); } } println(out,is,">> writing data for loop:" + loopname + " is complete"); } private static void println(printwriter out, string indentationstring, string line) { out.print(indentationstring); out.print(line); out.print("/n"); } // code removed for clarity |
如何在tomcat下用ihds
通过java代码访问层次数据集的便利性已经显现,包括命令行应用程序.一旦aspire在tomcat下被初始化,进一步说你就可以直接在你的网页上包括数据集啦.目前支持的格式有:标准的xml,xml对象,文本文件以及excel数据.将来的格式计划包括java类的定义将会何xml,xsd对象以及一般的html页面相匹配.在你想得到这些格式的网页之前,你必须要掌握如何在tomcat下初始化aspire.上面的评论摘自“improve your career with tomcat and aspire.”
如果你已经熟悉了上面的内容,那我们现在就进入下面的话题:
1. 在配置文件里把你的层次数据集的定义联接到一个url.
2. 通过一个指定的类型的数据格式调用这个url.
把下面的部分加到你已经定义的配置文件里:
| ################################### # ihdstesturl: linking to a url ################################### ihdstesturl=aspire://samples//ihds-test//ihds-default-html-template.html ihdstesturl.formhandlername=ihdstest request.ihdstest.form_handler.class_request.classname= com.ai.htmlgen.dbhashtableformhandler1 |
在aspire里有两部分和url的定义有关:数据源和数据转换.aspire可以利用jsp,xslt,或者标签来进行数据的转换.默认的转换―标签―需要一个包括标签的模板文件名.第一行包括定义了数据的转换文件第二行指定一个数据定义来调用用于下载这一行的类ihdstest.第三行本质上是和第一部分的第一行一样的作用.这种差异保证了aspire的向后兼容性.
确保负责转换的java类在属性文件里有明确的描述:
aspire有两种转换的方式,就是普通转换和特殊转换.上面是一个特殊转换的例子,因为描述的html模板是这个页面专用的.普通的转换方式能把任何的层次数据集转换维任何的页面.下面是一个普通转换方式的配置文件样式:
| # generic transform support # xml output generictransform.classic-xml.classname= com.ai.xml.formhandlertoxmltransform generictransform.object-xml.classname= com.ai.generictransforms.objectxmlgenerictransform # excel output generictransform.excel.classname= com.ai.generictransforms.excelgenerictransform # text generictransform.text.classname= com.ai.generictransforms.debugtexttransform |
这些定义经常被包括到aspire.properties主文件里.
invoke the url with a proper output format parameter
通过适当的输出格式参数调用一个url:
一旦url被定义.你就可以通过下面的路径看到结果的html页面了:
http://yourhost:yourport/your-webapp/servlet/displayservlet?url=ihdstesturl
这将产生一个html页面.如果我们想通过调用一个url来得到一个标准的xml格式的数据,我们只需要把下面的语句添加到上面的url当中:
&aspire_output_format=classic-xml
对于excel文件,格式如下:
&aspire_output_format=excel
主要就是将aspire_output_format参数和普通的java类名联系起来.写这些普通的转换来满足你的输出是非常简单的.下面是一个实现excel的普通转换的代码:
创建你自己的输出格式或者实现你自己的普通数据转换
| package com.ai.generictransforms; import com.ai.htmlgen.*; import com.ai.common.transformexception; import java.io.*; import com.ai.data.*; import javax.servlet.http.*; public class excelgenerictransform extends ahttpgenerictransform implements iformhandlertransform { private static string s_separator = "/t"; protected string getderivedheaders(httpservletrequest request) { return "content-type=application/vnd.ms-excel|content-disposition= filename=aspire-hierarchical-dataset.xls"; } public void transform(ihds data, printwriter out) throws transformexception { statictransform(data,out); } public void transform(iformhandler data, printwriter out) throws transformexception { statictransform((ihds)data,out); } public static void statictransform(ihds data, printwriter out) throws transformexception { try { writealoop("maindata",data,out,""); } catch(dataexception x) { throw new transformexception("error: excelgenerictransform: data exception",x); } } private static void writealoop(string loopname, ihds data, printwriter out, string is) throws dataexception { println(out,is, ">> writing data for loop:" + loopname); // write metadata imetadata m = data.getmetadata(); iiterator columns = m.getiterator(); stringbuffer colbuffer = new stringbuffer(); for(columns.movetofirst();!columns.isattheend();columns.movetonext()) { string columnname = (string)columns.getcurrentelement(); colbuffer.append(columnname).append(s_separator); } println(out,is,colbuffer.tostring()); //write individual rows for(data.movetofirst();!data.isattheend();data.movetonext()) { stringbuffer rowbuffer = new stringbuffer(); for(columns.movetofirst();!columns.isattheend();columns.movetonext()) { string columnname = (string)columns.getcurrentelement(); rowbuffer.append(data.getvalue(columnname)); rowbuffer.append(s_separator); } println(out,is,rowbuffer.tostring()); // recursive call to print children iiterator children = data.getchildnames(); for(children.movetofirst();!children.isattheend();children.movetonext()) { //for each child string childname = (string)children.getcurrentelement(); ihds child = data.getchild(childname); writealoop(childname,child,out,is + "/t"); } } println(out,is,">> writing data for loop:" + loopname + " is complete"); } private static void println(printwriter out, string indentationstring, string line) { out.print(indentationstring); out.print(line); out.print("/n"); } } |
层次数据集和tomcat开发团队下的aspire之间的联系
tomcat开发团队对这些工具之间的关联是非常感兴趣的.网页开发者可以把一系列的数据标识放在页面的头部,并允许用户输入自己首选的格式的数据,然后网页开发者就可以通过这种机制来获取数据.对于要用到电子数据表的终端用户来说,一个可以支持excel输出的机制是非常受欢迎的.b2b的用户可以得到xml格式的数据.java和别的开发人员可以把这些数据绑定起来,并以java类的格式得到他,然后就可以通过这个java类像对xml操作那样操作这些数据绑定.
所有提到的工具对于tomcat开发人员都是一个免费的很小的包.这就意味着,对于所有的学生和各种层次的开发者来说,他们可以免费下载一整套的tomcat和aspire以便和像dreamweaver之类的工具配合使用,并在任何他们所选择的数据库运用当中都会产生很好的效果.
随着学习经验的不断积累, 在这种结构的基础上,他们可以开始写一些plug-ins,甚至一些老练的java开发人员可以进行一些专用的开发.这种结构是一个通往学习java,j2ee,xml和企业开发的很好的梯子.
闽公网安备 35060202000074号