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 API的程序源碼:
下面的代碼將遍歷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和企業開發的很好的梯子.