2015年12月28日 星期一

DXL (Domino XML Language) 的應用

Notes的開發者都很清楚,Notes不像其他程式語言可以將元件個別存成不同檔案,而是將程式邏輯權限資料附檔全都打包成一個NSF檔案,且只有在安裝Notes後才可開啟。
其他語言在一個專案下可以切割成多個檔案維護

但最麻煩的,就是把所有程式資料綁在一起,造成切割困難,今天如果要將其中一個表單或一支代理程式分享,也只能打包成一個NSF檔後寄出,收件者再自行複製所需元件至目標資料庫,這過程實在太搞缸了,且在實現單一元件的版本安控來說是個很大的漏洞。
Notes在一個NSF檔中包含了所有的設計元件

再則另一個問題,雖然Notes程式對資料的控制十分完備,但對設計元件的控管卻相對荒蕪,除了開啟designer幾乎別無他法,而造成在設計時的彈性不足。

這困擾其實還是有改善空間的。為了加強對設計元件的控制,Notes很早就推出DXLDomino xml language),以XML為資訊交換的標準格式,進而加強了對文件及設計元件的新增修改功能,如前所提,文件控管其實沒有太多額外需求,所以我們將重心放在設計元件。

白話的說法就是,在程式執行時,要如何視需要而動態地新增agentformview甚至field

首先在安裝Notes時,安裝目錄下,lotus/notes/xmlschemas的資料夾路徑,便已包含了各個版本副檔名為.dtd的定義檔和.xsd的schema檔存放路徑的名稱便已透露了玄機,包含的是安裝版本以降的所有版本定義檔,在解析XML檔案時的前提就是,解析時的版本至少要高過匯出DXL時的版本,才能有對應的定義檔。這定義檔和Schema中所存放的,就是判別及解讀匯出的DXL的結構及定義。

以,正確的程序是,將要增加或修改的設計元件匯出為DXL格式,再將此DXL經由對應的DTD判讀後匯入,產生新的Notes設計元件。
範例為版本8.5.2,若版本為9.0.1則會包含9.0.1, 9.0, 8.5.3的版本

在這我們至少就碰到二個課題。該如何匯出DXL,以及該如何將DXL匯入。

我先假意地誇讚Notes,它貼心地提供了便利的匯出DXL工具,見下圖,以DXL匯出器直接匯出,以後如果有Notes只交付部分元件時,多了另一種選擇,可以直接傳送單一檔案供換版者匯入。

但接下來又引發了幾個問題,接收者該用什麼工具匯入DXL?以及最小的元件單位為何?這就算是敗筆了。Notes並未提供匯入工具,且最小匯入單位僅為表單(form)。Notes雖然沒有提供匯入工具,但好在不管是用JavaLS,都不是太困難,且若要動態新增或修改時,也不可能是由designer手動匯入,所以除非是如前面所提的,要將部分元件匯出再轉入,否則就算有匯入工具,大多也只是感受上的問題。

DXL的類別主要就是NotesDXLExporterNotesDXLImporter二個Class,分別為匯出及匯入,至於parser則是NotesDOMParserNotesSAXParser,差別在DOM將整份XML一次Load並暫存於記憶體來存取,效能自然比逐行循序讀取的SAX要高些,但還得視產出的XML檔案大小,來決定效能或資源之間的取捨。而DXL匯入的關鍵在,
Set importer = session.CreateDXLImporter(stream, db)
importer.Process

但匯出匯入讓人頭疼的重點肯定不在Code,而是若不使用Exporter時,該如何產生DXL檔?

提個簡單的範例,

這個簡單的表單,包含FormDescription_1二個欄位和一個TEST按鈕。我們試著如上面的DXL匯出器,可以得到以下的DXL檔案結果。

斷行整理一下(或可直接以browser開啟更容易閱讀),第一行代表了XML的宣告和編碼,接著<!DOCTYPE>是指定引用的DTD,用來規範及解讀匯出匯入的DXL檔案,如果在匯出和匯入有版本差異時,可以在此去修改解讀的DTD檔案版本。

宣告後的部份是Notes DB的屬性,有些無法以程式達成的DB屬性變更,也都可以在匯入DXL時直接取代。粗框圍起來的地方代表的是Notes的表單內容,全都包含在<body><richtext>的標記中。裡面的三個細框,分別是Form Description_1欄位,以及TEST按鈕。

而我現在想做手腳的是Description_1列,假設在匯出時,把Description依實際需求變動的狀況,動態產生新欄位XML Tag,Description_2欄位,如下圖。

接著再將此DXL欄案匯入,重新在Designer裡去開啟表單,會發現有些變化,多了一個Description_2的欄位了。
執行匯入後表單產生新的欄位Description_2

匯入時要小心的是,DXL匯入時是以NoteIDKey去取代,執行匯入時,並不會像Designer一樣貼心地提示將產生衝突文件,或因相同名稱而產生備存元件,而是毫不留情地立即取代現有的表單。這是因為DXL新增元件只能到表單,而不是欄位。但我們可以應用此方法,在XML對應欄位的地方,用程式加以判斷來新增欄位的XML tag,要幾個欄位就在DXL中加幾個tag

甚至在選擇產生新元件的部份,做個彈性的選項專用的子表單,讓使用者不需透過Designer,決定要新增幾個欄位、欄位名稱為何、欄位屬性,又或者更擴大範圍,可以決定新增或修改哪些View、呈現哪些Column、表單、頁面、子表單、共用欄位。。。等。這些在web上原本就能夠以HTML輕易達成,但HTML上的欄位只是個愰子,並不真的具備Notes的元件屬性,更別提Notes Client了。
Notes的欄位,就可以想成是HTML 的欄位,在迴圈中的
|<input type="text" name="Field_| + CStr(i) + |" value=.......
再加上能彈性地對應資料庫中的欄位Field_1, Field_2, Field_3.....等的綜合結論,這樣看來是不是就覺得比較厲害些?而這目標能以DXL輕易達成。

但我們的困難仍停留在如何隨心所欲地產生DXL檔案,以人力去比對dtd & xsd來產生DXL肯定會讓人消化不良,但我們可以參考一些簡單的定義及屬性,或更偷懶的方法是,在執行程式前,就先將原生表單(在尚未新增欄位前)先行以匯出器或檢視器做一DXL範本文字串存於Profile中,在程式執行時會先取得文字串,再加上程式判斷而加入欄位的tag,最後再加以XML的收尾,組合成完整的新DXL後,再重新匯入,而匯入後自然就取代了原來的表單。

因為是直接取代,如果重覆執行只會有相同的結果,並不會將變動的元件累加。所以在這表單到底現在已經有幾個欄位了,各是什麼屬性,程式是無法判讀的,必須記錄在Profile中,每次執行時才能夠一再地累加上去,不會永遠都只有一次新增的欄位。

這方法說穿了也挺傻眼,就像魔術被看穿一樣,但做大事不拘小節,拿出來嚇唬人是「挫挫有餘」。當然,這動作也要在點選後、存檔前能即時顯現,才能顯出其高大深遠及氣宇非凡。

可惜,講了半天,我的範例還是失敗了!!!!

所有的理論都能實現,唯獨即時顯現變動的元件這點,若是查看欄位屬性,其實我在Debug狀態可看到,UI已經產生Description_2了,但表單即使關閉再重新開啟欄位也未顯現,問題徵結隱藏在前面提過的, 

必須「重新在Designer裡開啟表單」或是「重新開啟資料庫」,設計才會被顯示,如下圖:

再說一次,不是重開文件,而是重新在Designer裡開啟表單,或重新開啟資料庫。

重新在Designer裡開啟表單,或重新開啟資料庫。

這麼機歪所以說了三次,有點無奈,還在思考其他的解決方法。未來有結果再陸續更新。