<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    lbom

    小江西

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      18 隨筆 :: 21 文章 :: 69 評論 :: 0 Trackbacks

    #

        幾天前,偶爾和鄰居聊天,她說要去買頂蚊帳過夏,不由的也動了心。是啊,在小時候,那家不是用蚊帳來保證漫長夏的良好睡眠呢!現在隨著科技發達,家家戶戶,特別是城市住戶都已經將蚊帳扔掉而改用蚊香或蚊片了。
        于是等夫人下班回家,和其商量,卻是死活不同意,理由如下:1)影響臥室美觀;2)擠占空間;3)拆洗不便。。。。。。
        沒辦法,為了達到目的,我只得絞盡腦汁,想出各種理由,以期望能夠說服夫人:
        1)身體牌:我對蚊香有過敏,因此,在夏天,我們是不能點蚊香的;再說,各種滅蚊產品其主要成份都有毒性,不管其含量多少,都對身體無益。
        2)經濟牌:在重慶,一年有6個月的夏天,以每晚一片滅蚊片計算,一年下來,加上電熱滅蚊器最少需要投入120元以上才能保證夏季無憂,三四年下來,就可以買個較好的蚊帳了;再說,滅蚊片用久后,還得防止蚊子產生的抗藥性;但使用蚊帳卻不需要用電,一年下來,其電費也能節約不少,還可防止可能因電熱滅蚊器散熱不良而導致的用電風險的發生;
        3)環保牌:在臥室支個蚊帳,即溫馨又浪漫,還無滅蚊產品的各類化學合成的氣味,最環保不過了;
        4)衛生牌:在床上支個蚊帳,將有效的減少灰塵降落,也免了蜘蛛等小蟲在晚間不意間打擾我們的安眠,多好的一件事!
        。。。。。。
        啰啰嗦嗦,直說的口干舌燥,并許下一堆好處之后,終于換來老婆的點頭。于是二話不說,拉上夫人,直奔商場,在東挑西比之后,買下一款合意的落地式蚊帳。
        至此,我的環保蚊帳計劃就此實現!
     
        所以,回歸和懷舊,并不都是倒退!
    posted @ 2009-05-26 09:43 lbom 閱讀(294) | 評論 (0)編輯 收藏

            在一周前,項目組碰到一個大問題:NTKO Office Activex控件在上傳文件及提交頁面信息時,其提交的頁面元素輸入中文值變成了無法識別的、也不屬于已知編碼中的任何一種編碼格式的亂碼;但在NTKO Office Activex控件包裝項目組提供的的測試項目中,此問題并未出現,因此判斷是項目兼容性所導致。
           在項目組功能開發員和控件包裝組成員進行近一周的努力后,也未解決此問題。最后,此問題交由我來做最后分析和處理。
           經過三天時間對問題項目的分拆、組裝、分析、測試后,終于找到問題所出,現將此過程進行記錄,以備參考:
            1)以控件包裝組測試項目為基準,建立項目級測試項目,并保證在此測試項目中不存在兼容性問題;
            2)檢測web.xml:將問題項目的web.xml代替測試項目中的web.xml,未出現兼容性問題,從而排除因web.xml的差異而導致的兼容性;
            3)測試問題項目中的項目依賴:將問題項目的項目依賴關系復制至測試項目中,發現兼容性問題未出現,從而排除項目依賴導致的兼容性;
            4)檢測支持包:將問題項目中的支持包(各jar)代替測試項目中的支持包,未出現兼容性問題,從而排除因支持包的差異導致的兼容性,也就排除了各servers,servlet,listener等導致的兼容性問題;
            5)檢測js支撐:將問題項目中的所有相關js文件取出,代替測試項目中的相關js文件,未出現兼容性問題,從而排除因js支持文件的差異導致的兼容性;
            6)檢測css支撐:將問題項目中的所有相關css文件取出,代替測試項目中的相關js文件,未出現兼容性問題,從而排除因css支持文件的差異導致的兼容性;
            7)檢測tld,xml文件:將問題項目中的tld,xml文件取出,代替測試項目中的tld,xml文件,未出現兼容性問題,從而排除因tld,xml的差異導致的兼容性;
            8)至此,正常解決的兼容性措施都已用完,還是未找到問題所出!如何辦?
            9)開始使用非正常手段進行排查:
                <1>對比檢查.project和.classes未發現異常,從而排除基本項目配置導致的兼容性;
                <2>將問題項目的web項目設置文件(.settings)代替測試項目的web項目設置文件(.settings),問題出現了!繼續排隊分析,發現問題出現在文件org.eclipse.wst.common.component中,
    問題項目的設置為:
                                        <?xml version="1.0" encoding="UTF-8"?>
                                        <project-modules id="moduleCoreId" project-version="1.5.0">
                                           <wb-module deploy-name="XXX_IC">
                                           <wb-resource deploy-path="/" source-path="/web"/>
                                           <wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
                                           <wb-resource deploy-path="/WEB-INF/classes" source-path="/test"/>
                                           <property name="java-output-path" value="build/classes"/>
                                           <property name="context-root" value="XXX_IC"/>
                                       </wb-module>
                                       </project-modules>
    測試項目設置為:
                                        <?xml version="1.0" encoding="UTF-8"?>
                                        <project-modules id="moduleCoreId" project-version="1.5.0">
                                           <wb-module deploy-name="test">
                                           <wb-resource deploy-path="/" source-path="/web"/>
                                           <wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
                                           <wb-resource deploy-path="/WEB-INF/classes" source-path="/test"/>
                                           <property name="java-output-path" value="build/classes"/>
                                           <property name="context-root" value="test"/>
                                       </wb-module>
                                       </project-modules>
    且無論如何修改"XXX_IC",都會導致兼容性出現,最后沒辦法,將下劃線"_"去掉,奇跡出現了。
            原來NTKO Office Activex控件在提交數據時,是通過scoket模擬Http進行文件和頁面元素的提供,如提交的頁URL完整路徑中包含了"_"等字符時,將導致無法識別,從而導致兼容性的產生。
    posted @ 2009-05-15 22:13 lbom 閱讀(1509) | 評論 (2)編輯 收藏

     

    從小到大,椰子已經吃過很多次了,但在這些吃椰子有經歷中,我只知道一種吃法:開孔à插吸管à喝椰汁à丟椰殼à完事!

           但在今天,事情有了些變化,于是產生的椰子的第二種吃法。

    我和夫人在散步時,順便準備到超市買點水果,看到水果區的椰子又大又好且正在打特價。心中一動,就挑了個大的,買回家準備細細品嘗一番。在按通常吃法吸光椰子中的水汁后,突然想起,我們平時很喜歡吃超市中的一種叫椰角小吃的,又甜又韌很有嚼勁,但椰角是長大哪的呢?不會是椰子樹上的另一種產品吧!看著椰子開口的硬殼下面的白色軟組織,我們就突發奇想,這白色軟組織會不會就是那椰角呢?

    說動就動,先用刀將空椰子殼砍開,發現其內層確實是一層約0.8cm厚的白色果肉。小心的切下一小塊嘗一嘗,味道淡淡的,很有韌勁,確實就是那椰角的源料。這就是我發現的椰子的第二種吃法了。

    在生活中有很多事:你沒經歷,就不會想到;當你想不到時,美好的事情就可能錯過!
        這就是生活。

                                                                                        2009/5/5

    posted @ 2009-05-05 22:31 lbom 閱讀(2373) | 評論 (2)編輯 收藏

     

    在我們公司的軟件研發體系中,存在著三種截然不同的軟件開發方式。而我,作為公司最老同事之一,也是這三種開發模式的親歷者,曾不只一次的被公司同事問過我關于這三類方式之間的異同點。于是利用空閑時間,對其進行一番整理、分析和對比。

    1、全能型

    部門經理在接到項目之后,將此項目交給部門內的熟練程序員后,此程序員就自動被委任其為項目經理。從此開始,程序員將根據項目售前方案和銷售合同內容,在項目進行過程中分別擔當起項目經理、功能設計師、數據存貯設計師、程序員、測試員和項目實施人員等諸多角色。并在項目進行的過程中,帶領少量其它程序員和輔助資源來完成此項目的所有工作。

    此類項目其功能單一且不復雜,只是為了幫助用戶提升某一項工作的工作效率或解決客戶在其工作中的一些問題,如工作日志信息的采集和分析業務項目、辦公用品的申請和領用等。它們因為其涉及范圍小,使用人員不多,從而具有項目總費用少、開發和實施周期短、對性能要求不高的特點。

    在此類開發模式中,程序員由于其工作的全面性,使他們在進入項目組后,能夠得到很快且全面的提升,并會在與客戶交往的過程中,建立起良好的客戶關系處理經驗,為其今后的成長和發展打開良好的基礎。

    由于項目需要,程序員需要掌握全面技能,容易造成其在項目開發過程中需要全面的接觸項目管理及人際關系、需求分析、數據庫及對象設計、人機交互和用戶體驗設計、系統設計和開發、測試和系統提升、應用實施和售后維護等諸多截然不同的領域范圍;所以,作為此類程序員,其工作壓力之大,事務之復雜、綜合素質要求之高,是其它模式的程序員所無法對比的,這也是造成此類項目按時完成率極低、尾款回收困難、項目售后工作難做、用戶滿意度差、二次項目獲取困難的根本原因。

    同時,由于程序員被大量的非開發性事務所干擾,造成他們無法專心致力于專業技能的學習和提升,也就無法造就一支高效率、高穩定性、配合默契的開發隊伍,這也是造成公司內此類人才大量流失的重要原因。

    2、英雄型

           部門經理在接到此類項目后,按項目所涉及的領域范疇,將其按領域進行分工。以企業信息協同系統為例,我們將進行如下分工:門戶信息的獲取、聚合、交互和展現工作交給專職于門戶開發的程序員;內部郵件系統的分析、設計、和實現將給郵件開發程序員;日程和事務的設計交給日程開發程序員;工作流應用工作交由工作流客戶化開發程序員等等。

           在此類型的開發模式中,程序員將會是某一領域內的英雄式人物!由于他長期且相對穩定的負責著這個有限領域范圍內的一切事務,可以幫助他在一定時期內進行系統而穩定的業務研究和分析工作,進而成長為此領域內的業務專家。而且,通過持續的對其工作進行迭代式開發、升級和完善,可使此產品在產品品質、適用性和用戶體驗等方面得到穩定的提升,進而提升了整個產品的品質。

           如果此領域內的工作產品能夠得到合理的規劃和實現,進而將其進行單獨的封裝、應用集成和推廣,就有可能形成一個具有相當競爭力的產品,從而為公司獲取新的銷售機會和利潤點。

           但是,此類開發模式中的分工也容易造成程序員涉及業務領域單一和適應性窄的缺陷:由于其長期面對和研究著單一業務領域內的業務活動,而無法更多的接收和參考來自于用戶、企業和其它行業內的非它業務發展需要和趨勢,從而對其在產品領域內的發展產生限制,并造成其產品方向上的不準確或錯誤定位;由于其長期的在單一領域內工作,并在此領域內獲得了公司內的認可,這也將限制他在領域間的流動性。當公司或部門的產品方向和需要調整和改變時,此類程序員就需要被迫改變甚至放棄其在原領域內的所有積累而重新開始,從而造成巨大的浪費。

    3、專業型

           項目經理在接到項目之后,根據項目組成員的能力、特長職業規劃,對他們進行適當且專業化分工:由業務規劃人員負責項目的需求收集、業務規劃和需求分析;由系統架構師對系統的進行技術構架和支撐性技術的規劃和引進;由數據庫專員負責數據庫對象的設計和性能調優;由功能分析員在人機交互人員輔助下負責功能設計和人機交互模式;由業務邏輯實現專員根據功能設計進行高性能的業務邏輯處理實現和外部接口的設計和實現;由頁面開發人員負責實現人機交互;測試人員負責對系統進行全過程的測試和質量監督;專業化實施人員可快速高效的進行系統實施和在線維護,售后服務工作也將由專人負責;

           通過恰當和合理的分工,將軟件研發過程中的各個環節進行拆分,從而將復雜的軟件工程分解成一個個相對獨立且又緊密關聯的工作項,從而有效的降低了軟件開發過程中的困難度和風險性;項目經理把分解后的工作項交給項目組中的合適項目成員,并根據項目組成員的能力、工作難度和工作量,制定出科學的項目計劃;同時,項目組成員在項目經理的協調和管理下進行密切的分工合作,此舉即能調動項目組成員的其工作積極性,又能使他們將工作、興趣和個人職業成長規劃進行有效的結合,從而使其在技能、收入和社會認可度等諸多方面得到快速成長,達到人盡其材,材盡其用的目的。通過使用專業的人做專業的事,公司將在人員分工、資源使用和業務拓展等領域走向專業化、規?;?,最終成為專業且強大的產業實體。

           但此開發模式也具有相當的局限性!其一,如何合理的利用項目組資源?項目組成員因其性格、能力和興趣各有不同,如何能將他們按項目分工和角色組成需要,進行專業化訓練和培養;其二,因項目組成員長期單一職能的工作,與其它環節的交叉和交流都受到限制,對其未來的全面發展和綜合成長都很不利;其三、各角色之間的分工、合作與工業化生產中的生產線相似,那么,建立與之相適應的質量保證體系,保證各工序之間生產產品的質量,從而從事實上提升軟件產品的整體質量?

           通過對這三類開發模式的分析,我們可以看出,它們各有合理性,也又具有的相當的局限性。

           全能型開發模式是早期的CS類項目開發的主要模式,其適用于哪些規模小,程序員少的小規模IT開發企業進行小型項目的開發中。但對于那些工期較長、業務范疇廣、復雜度較大的項目,此種開發模板將采用將導致風險最大化,失敗幾乎是其唯一的結局。

           英雄型開發模式,因項目組成員領域化的分工和合作,使它在通用型復合類產品開發中具有優勢。通過對產品的各組成部份進行持續的改進和迭代性開發,使產品在功能、性能、用戶體驗等方面得到持續的改善和提升,從而有利于產品拓展并在此過程中做大做強,最終取得競爭優勢。但此開發模式也將導致項目組成員之間的工作協調、技術互用等方面存在諸多不便;另外,因領域的專業性和不可替換性,也就限制了公司在處理關健人員的流動性方面存在諸多困難,并在核心競爭力的保證方面存在著很大風險。

           專業型開發模式,通過對人員進行專業化分工,從而在軟件開發過程中最大的利用了人力資源,提升軟件的生產效率,并降低了軟件的從業門檻。此方式在新形式下的項目開發和產品研發中都具有相當的競爭力,也易有利于保證公司的核心競爭力。但采用此種開發模式時,需要完善內部的人員激勵機制,保證各角色的從業人員都有與之適應的職位規劃和發展模式,并能根據項目組成員所處階段的需要,提供相應的技能培訓和交流機會,從而促進其成長,激勵其上進。

           總之,采取何種開發模式,要根據公司的實際業務情況,發展規劃和人員構成,進行科學的分析之后,再采取行動、從而得到具有延續性和競爭性,并與自身相匹配的軟件開發模式。

                                                                   2009/4/20

    posted @ 2009-04-21 21:52 lbom 閱讀(1880) | 評論 (2)編輯 收藏

             

    今天,在陪夫人逛街回來的路上,看到一幕慘劇的發生:一位中年男子,從家中跳樓而下,墜落于堅硬的水泥地面上,當場身亡!

    人生為何?為已?為親?還是為他?

    人生為已,就應該珍惜自己的生命,為自己這短暫的一生中,充實、幸福和快樂的活著,而不能因為暫時的挫折、失敗而絕望和放棄;人生漫漫而無現存路,需要自己去探索和開拓,在此過程中,必然會經歷曲折和無法避免的挫折。但是,不經歷風雨,如何能見彩虹!挫折過后,往往就伴隨著一段平坦的直道;人生艱難,生活、事業、家庭、社會各種矛盾在我這會聚,理不清也扯不斷!既然如此,何不干脆看開,將不可調和的矛盾進行暫時地休眠,讓時間這個解決矛盾的最好的潤滑劑來慢慢解決它。

    人生為親,為父母:他們含辛茹苦的將你從無到有、從幼養成,而你卻要在他們需要照顧和看護的年齡離他們而去!你忍心嗎!為妻:相汝以沫幾十年、同床共枕伴一生!鍋碗瓢盆一屋住,酸甜苦辣是生活。你就忍心在她人生路中間,正需你堅強的肩膀作支撐時,卻拋下她一人孤苦的走在這漫長的人生路上,你安心嗎?人生為子:父親是兒女的榜樣和偶像,他們需要借助你那成熟的智慧來打開事業的大門,也需要你那豐富的閱歷來開拓自己的人生路,更需要你成功的經驗來保護雛鳥并解決初飛時所遇到的種種風險!在這種關鍵的時候,你卻拋棄了他,你放心嗎?

    人生為他,為朋友兄弟:有多少美好時光值得回憶,有多少美妙經歷值得回味,又有多少坎坷擔當值得珍惜!你就此離開,從此兄弟聚首少一人,朋友舉杯缺一環!為事業:你正值人生當年,恰逢事業當期,失敗你經歷,成功應有你,酸甜苦辣都嘗遍,還有什么過不去?為社會:當今社會多少不平事,何必事事放我心!不必為人先,也不全落后;比上我不足,哪我就比下,實在比不過,阿Q一把也不錯!

    生命如此脆弱,稍有不慎就將墜落:走在路上被車撞死,走下樓下被東西砸死,乘車墜橋而死,去醫院被錯藥藥死,上班被累死,下班被煩死,既然如此,何必再來一個自己尋死呢!

    請珍惜生命,愛護自己!

     

    20090330于家中

    posted @ 2009-04-13 15:51 lbom 閱讀(137) | 評論 (0)編輯 收藏

            項目組使用潤乾報表已一年多了,說實話,潤乾報表在國內同類產品中屬于非常不錯的最好的報表開發和應用產品。相應的支持也比較到位,使用人員及交流社區也開展的很合適。在這先給它們作個廣告?。?!
            在項目中使用潤乾報表,對數據進行專業的報表應用和開發,我對其作簡單總結:
            1)對其服務器運行系統進行項目性客戶化開發,從而利用項目中的權限管理和模塊,實現對報表進行訪問控制。否則,這對企業級應用將是一個非常大的考驗。
            2)潤乾報表自帶的參數生成模塊、報表運行載體的樣式、風格都極其簡陋,與項目的實際風格可能存在很大的差距。因此必需對其進行深入的擴展和開發。我們項目組的經驗就是單獨開發參數生成模塊和潤乾報表載體,如此才保證了報表中心與項目的用戶體驗和交互性的一致性。
            3)為了更好的利用項目組資源,我們將潤乾報表開發人員獨立出來,形成專門的報表開發團隊。此團隊負責根據業務的需要,利用潤乾報表開發工具進行報表開發,即開發.raq報表文件。此部份人員可從項目組的普通成員和新進人員中進行培養,而無需占用大量的項目組中中高級開發人員資源,從而節約了項目組的資源。
            4)潤乾報表對過JSP標簽包含在jsp頁面中進行加裁我運行。我們稱此jsp頁面為潤乾報表運行載體。我們根據潤乾報表的運行載體進行了科學的分類,并根據分類開發出統一的報表運行載體頁面(jsp)。從而避免針對每個報表文件而開發與之對應的運行載體。此舉也大為減少了項目組的JSP開發人員的工作量。
            5)建立潤乾報表運行專用配置文件,將報表參數生成模塊、運行載體及潤乾報表三者之間的關系進行配置化管理,并以此為紐帶,將潤乾報表開發人員、JSP開發人員(開發報表運行載體和報表參數生成功能)聯系起來。

             在開發過程中,我們碰到并解決了如下問題:
             1)填報類潤乾報表在進行數據驗證時,其提示信息(以js的alert("...")方式提示用戶)成亂碼顯示:此問題是由于潤乾報表在V4.1以后,統一使用UTF-8作編譯編碼。因此,要解決此問題,需要將項目的編碼也改成UTF-8
             2)在潤乾報表的參數賦值需按序依次進行賦值,而不能采用參數名進行統一賦值。因為,如果在潤乾報表的SQL中使用了重復的參數進行賦值時,會報參數找不至的錯誤。
             3)在參數生成模塊中將中文參數值傳遞給潤乾報表時,會導致少量的參數值在傳遞過程中發生改變,如“機油”變成了“箕油”。此問題是由于在urlEncode和urlDecode的bug導致,請在開發時需特別注意。我們是通過自己對信息進行加碼和解碼來解決此bug。
             4)潤乾報表的運行環境與應用服務器的編碼方式有關:我們項目和報表中心的編碼方式為UTF-8,但運用服務器(WAS6.1)的編碼方式為GBK時,通過參數生成功能將中文參數傳遞給潤乾報表時,會出現亂碼問題。在將WAS的輸出和運行編碼改成UTF-8后,才解決此問題。


    posted @ 2009-03-05 23:08 lbom 閱讀(8014) | 評論 (24)編輯 收藏

            前段時間,項目組安排同事進行項目移植,并考慮在其過程中進行技術預研等相關工作,以對項目進行優化;
            在此過程中,有同事誤解面向對象化開發的精髓,在匆匆了了解JavaScript面向對象的方法和示例后,對項目中的公共門戶頭以對象的方式進行重寫。結果,將原30行的單一文件代碼變成了400多行,并分布于多個文件了。
            在拿到此結果之后,我是哭笑不得,于是得出了:“新技術的引入必需能夠提高生產效率或降低工作難度,否則,沒有引入的必要”這句話。

    posted @ 2009-02-21 20:52 lbom 閱讀(1323) | 評論 (3)編輯 收藏

     

    總是聽說Vista在軟件兼容性上有諸多問題,且一直未得到很好的解決,由于一直使用XP,對此也就不太在意。但是,因工作因素,需要升級我的飯碗(購買新筆記本,操作系統為vista)后,麻煩果然來了:

         先是Oracle數據庫安裝不了。還好,我在開發時可以使用公司的數據庫進行開發,不在本機安裝數據庫還可節約一筆硬盤空間和內存。故其影響并不大,只是在下班離開公司后,沒時使用數據庫而已。

         安裝Eclipse,繼續java項目開發,未發現兼容性問題;

         成功安裝Tomcat(版本號為5.5.17),但在啟動時,發現其只能用管理員身份進行啟動,而無法向往常一樣,通過開始菜單直接啟動。進入Eclipse,啟動項目(WebApplication),發現麻煩來了,不管我用何種方式,TomcatServer一直報服務超時,不能正常啟動!

         唉,難道要我重新恢復XP嗎,這可不是一張恢復盤的問題,而是我花了兩天時間,進行操作系統和相關相關軟件安裝,我的媽也!!

         到網上查找相關資料,也未獲取明確的解決之道;到MS支持網站,沒找到合適的方案;問周邊同事,得到N種可能的解決方式;經過一天時間,逐個試驗,終獲解決之道。

          可在環境操作系統變量中添加classpath項,其值如:C:/Program Files/Java/jdk1.5.0_11/lib;C:/Program Files/Java/jdk1.5.0_11/lib/tools.jar

    posted @ 2007-12-29 14:52 lbom 閱讀(4310) | 評論 (8)編輯 收藏

    昨日,在將應用程序(JSF應用,其中包含Tiles包)發布至測試服務器(Solaris8+Tomcate5.5)時,發現其不能正常運行,其錯誤如下:
    ......
    2006-11-1 17:09:54 org.apache.tiles.servlets.TilesServlet init
    信息: Initializing TilesServlet
    2006-11-1 17:09:54 org.apache.tiles.servlets.TilesServlet readFactoryConfig
    信息: CONFIG FILES DEFINED IN WEB.XML
    信息: initializing definitions factory...ets.TilesServlet initDefinitionsFactory
    2006-11-1 17:09:54 org.apache.tiles.servlets.TilesServlet saveExceptionMessage
    警告: Caught exception when initializing definitions factory
    2006-11-1 17:09:54 org.apache.tiles.servlets.TilesServlet saveExceptionMessage
    警告: I/O Error reading definitions.
    2006-11-1 17:09:54 org.apache.tiles.servlets.TilesServlet saveExceptionMessage
    s.
    2006-11-1 17:09:54 org.apache.tiles.servlets.TilesServlet saveExceptionMessage
    警告: Caught exception when initializing definitions factory
    2006-11-1 17:09:54 org.apache.tiles.servlets.TilesServlet saveExceptionMessage
    警告: I/O Error reading definitions.
    2006-11-1 17:09:54 org.apache.tiles.servlets.TilesServlet saveExceptionMessage
    警告: javax.servlet.ServletException: I/O Error reading definitions.
    2006-11-1 17:09:55 org.apache.coyote.http11.Http11BaseProtocol start
    信息: Starting Coyote HTTP/1.1 on http-8800
    ......

    根據此錯誤分析,是由于TilesServlet未正確讀取tiles.xml配置文件,但在對tiles.xml進行權限變更后,也未解決此問題!!!
    但是,此應用在開發環境下是正常的,如何是好??
    我和同事在對比開發環境和測試環境之后,發現二者的運行環境差別只有操作系統(UNIX<>WINDOWS XP);
    搜索Google和BeiDu,找到一篇相類似的報到,據其所說,當他的系統在斷開網絡后會出現類似的情況,難道是TilesServlet必需聯上互聯網?
    在分析tiles.xml后,發現,其中有如下一句:
    ???<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN" "原來,當互聯網斷開之后,不能從tiles-config_2_0.dtd中獲取驗證,導致此文件解釋失敗,將此刪除之后,系統就可正常部屬在測試環境之中了.


    懷疑論者的 JSF: JSF 組件開發

    省時運動使得構建 JSF 組件輕而易舉

    developerWorks
    文檔選項
    將此頁作為電子郵件發送

    將此頁作為電子郵件發送

    未顯示需要 JavaScript 的文檔選項

    Discuss

    Sample code


    對此頁的評價

    幫助我們改進這些內容


    級別: 中級

    Rick Hightower , CTO, ArcMind

    2005 年 8 月 16 日

    在四部分的 懷疑論者的 JSF 系列的最后一期中,Rick Hightower 介紹了省時運動,它可以一次或永遠地說服您:JSF 組件開發要比您想像的更容易。

    組件模型的關鍵考驗就是:能否從第三方供應商購買組件,并把它們插入應用程序?與可購買可視 Swing 組件一樣,也可以購買 Java ServerFaces (JSF) 組件!需要一個好玩的日歷?可以在開源實現和商業組件之間選擇??梢赃x擇購買一個,而不是自行開發復雜的基于 Web 的 GUI 組件。

    JSF 擁有一個與 AWT 的 GUI 組件模型類似的組件模型??梢杂?JSF 創建可重用組件。但不幸的是,存在一個誤解:用 JSF 創建組件很困難。不要相信這些從未試過它的人們的 FUD!開發 JSF 組件并不困難。由于不用一遍又一遍重復相同的代碼,可以節約時間。一旦創建了組件,就可以容易地把組件拖到任何 JSP、甚至任何 JSF 表單中,如果正在處理的站點有 250 個頁面,這就很重要了。JSF 的大多數功能來自基類。因為所有的繁重工作都由 API 和基類完成,所以 JSF 把組件創建變得很容易。

    貫穿這個系列,我一直在試圖幫助您克服造成許多 Java 開發人員逃避使用 JSF 技術的 FUD。我討論了對這項技術的基本誤解,介紹了它的底層框架和它最有價值的開發特性。有了這些基礎工作之后,我認為您已經可以采取行動,開發自己的定制 JSF 組件了。使用 JSF 的東西,我敢保證要比您想像的要更加容易,而且從節約的時間和精力上來說,回報如此之多,多得不能忽略。

    這篇文章中的示例是用 JDK 1.5 和 Tomcat 開發的。請單擊頁面頂部的 示例代碼 下載示例源代碼。注意,與以前的文章不同,這篇文章沒有關聯的 build 文件,因為我特意把它留給您作為一個練習了。只要設置 IDE 或編譯器,把 /src 中的類編譯到 /webapp/WEB-INF/classes,并在 /webapp/WEB-INF/lib 中包含所有 JAR 文件(以及 servlet-api.jarjsp-api.jar,它們包含在 Tomcat 中)。

    JSF 組件模型

    JSF 組件模型與 AWT GUI 組件模型類似。它有事件和屬性,就像 Swing 組件模型一樣。它也有包含組件的容器,容器也是組件,也可以由其他容器包含。從理論上說,JSF 組件模型分離自 HTML 和 JSP。JSF 自帶的標準組件集里面有 JSP 綁定,可以生成 HTML 渲染。

    JSF 組件的示例包括日歷輸入組件和 HTML 富文本輸入組件。您可能從來沒時間去編寫這樣的組件,但是如果它們已經存在,那會如何呢?通過把常用功能變成商品,組件模型降低了向 Web 應用程序添加更多功能的門檻。

    組件的功能通常圍繞著兩個動作:解碼和編碼數據。解碼 是把進入的請求參數轉換成組件的值的過程。編碼 是把組件的當前值轉換成對應的標記(也就是 HTML)的過程。

    JSF 框架提供了兩個選項用于編碼和解碼數據。使用直接實現 方式,組件自己實現解碼和編碼。使用委托實現 方式,組件委托渲染器進行編碼和解碼。如果選擇委托實現,可以把組件與不同的渲染器關聯,會在頁面上以不同的方式渲染組件;例如多選列表框和一列復選框。

    因此,JSF 組件由兩部分構成:組件和渲染器。JSF 組件 類定義 UI 組件的狀態和行為;渲染器 定義如何從請求讀取組件、如何顯示組件 —— 通常通過 HTML 渲染。渲染器把組件的值轉換成適當的標記。事件排隊和性能驗證發生在組件內部。

    在圖 1 中可以看到數據編碼和解碼出現在 JSF 生命周期中的什么階段(到現在,我希望您已經熟悉 JSF 生命周期了)。


    圖 1. JSF 生命周期和 JSF 組件
    JSF 組件和 JSF 生命周期
    提示!

    在許多情況下,可以在保持組件本身不變的情況下,通過改變渲染而簡化開發過程。在這些情況下,可以編寫定制渲染器而不是定制組件。

    更多組件概念

    所有 JSF 組件的基類是 UIComponent。在開發自己的組件時,需要繼承 UIComponentBase,它擴展了 UIComponent 并提供了 UIComponent 中所有抽象方法的默認實現。

    組件擁有雙親和標識符。每個組件都關聯著一個組件類型,組件類型用于在 face 的上下文配置文件(faces-config.xml)中登記組件??梢杂?JSF-EL (表達式語言)把 JSF 組件綁定到受管理的 bean 屬性??梢园驯磉_式關聯到組件上的任何屬性,這樣就允許用 JSF-EL 設置組件的屬性值。在創建使用 JSF-EL 綁定的組件屬性時,需要創建值綁定表達式。在調用綁定屬性的 getter 方法時,除非 setter 方法已經設置了值,否則 getter 方法必須用值綁定獲得值。

    組件可以作為 ValueHolderEditableValueHolder。ValueHolder 與一個或多個 ValidatorConverter 相關聯;所以 JSF UI 組件也與 ValidatorConverter 關聯(請參閱 參考資料 獲得更多關于 JSF 驗證和轉換的內容。)

    像表單字段組件這樣的組件擁有一個 ValueBinding,它必須綁定到 JavaBean 的讀寫屬性。組件可以調用 getParent 方法訪問它們的雙親,也可以調用 getChildren 方法訪問它們的子女。組件也可以有 facet 組件,facet 組件是當前組件的子組件,可以調用 getFacets 方法訪問它,這個方法返回一個映射。Facets 是著名的子組件。

    這里描述的許多組件的概念將會是接下來展示的示例的一部分,所以請記住它們!



    回頁首


    JSF 樣式的 Hello World!

    我們用一個又好又容易的示例來開始 JSF 組件的開發:我將展示如何渲染 Label 標記(示例:<label>Form Test</label>)。

    下面是我要采取的步驟:

    1. 擴展 UIComponent
      • 創建一個類,擴展 UIComponent
      • 保存組件狀態
      • 用 faces-config.xml 登記組件
    2. 定義渲染器或者內聯地實現它
      • 覆蓋 encode
      • 覆蓋 decode
      • 用 faces-config.xml 登記渲染器
    3. 創建定制標記,繼承 UIComponentTag
      • 返回渲染器類型
      • 返回組件類型
      • 設置可能使用 JSF 表達式的屬性

    Label 示例將演示 JSF 組件開發的以下方面:

    • 創建組件
    • 直接實現渲染器
    • 編碼輸出
    • 把定制標記與組件關聯

    返回 圖 1,可以看到在這個示例中會有兩個生命周期屬性在活動。它們是 Apply Request ValueRender Response

    在圖 2 中,可以看到在 JSP 中如何使用 Label 標記的(<label>Form Test</label>)。


    圖 2. 在 JSP 中使用 JSF 標記
    在 JSP 中使用 JSF 標記

    第 1 步:擴展 UIComponent

    第一步是創建一個組件,繼承 UIOutput,后者是 UIComponent 的子類。 除了繼承這個類之外,我還添加了組件將會顯示的 label 屬性,如清單 1 所示:


    清單 1. 繼承 UIComponent 并添加 label
    
    import java.io.IOException;
    
    import javax.faces.component.UIOutput;
    import javax.faces.context.FacesContext;
    import javax.faces.context.ResponseWriter;
    
    public class LabelComponent extends UIOutput{
    
    	private String label;
    
    	public String getLabel() {
    		return label;
    	}
    	public void setLabel(String label) {
    		this.label = label;
    	}
    ...
    

    接下來要做的是保存組件狀態。JSF 通常通過會話、隱藏表單字段、cookies 等進行實際的存儲和狀態管理。(這通常是用戶配置的設置)。要保存組件狀態,需要覆蓋組件的 saveStaterestoreState 方法,如清單 2 所示:


    清單 2. 保存組件狀態
    
        @Override
        public Object saveState(FacesContext context) {
            Object values[] = new Object[2];
            values[0] = super.saveState(context);
            values[1] = label;
            return ((Object) (values));
        }
    
        @Override
        public void restoreState(FacesContext context, Object state) {
            Object values[] = (Object[])state;
            super.restoreState(context, values[0]);
            label = (String)values[1];
        }
     

    可以注意到,我使用的是 JDK 1.5。我對編譯器進行了設置,所以我必須指定 override 注釋,以便指明哪些方法要覆蓋基類的方法。這樣做可以更容易地標識出 JSF 的鉤子在哪。

    創建組件的最后一步是用 faces-config.xml 登記它,如下所示:

    
    <faces-config>
    
       <component>
          <component-type>simple.Label</component-type>
          <component-class>
             arcmind.simple.LabelComponent
          </component-class>
       </component>
    ...
    

    第 2 步:定義渲染器

    下面要做的是內聯地定義渲染器的功能。稍后我會介紹如何創建獨立的渲染器?,F在,先從編碼 Label 組件的輸出、顯示 label 開始,如清單 3 所示:


    清單 3. 編碼組件的輸出
    
    public class LabelComponent extends UIOutput{
    	...
    	public void encodeBegin(FacesContext context) 
    					throws IOException {
    
    		ResponseWriter writer = 
    			context.getResponseWriter();
    
    		writer.startElement("label", this);
            	            writer.write(label);
            	            writer.endElement("label");
            	            writer.flush();
    	}
    	...
    }
    

    注意,響應寫入器(javax.faces.context.ResponseWriter)可以容易地處理 HTML 這樣的標記語言。清單 3 的代碼輸出 <label> 元素體內的 label 的值。

    下面顯示的 family 屬性用來把 Label 組件與渲染器關聯。雖然目前 Label 組件還不需要這個屬性(因為還沒有獨立的渲染器),但是在這篇文章后面,在介紹如何創建獨立渲染器的時候,會需要它。

    
    public class LabelComponent extends UIOutput{
    	...
    	public String getFamily(){
    		return "simple.Label";
    	}
    	...
    }
    

    插曲:研究 JSF-RI

    如果正在使用來自 Sun Microsystems 的 JSF 參考實現(不是 MyFaces 實現),那么就不得不在組件創建代碼中添加下面一段:

    
    public void encodeEnd(FacesContext context) 
    			throws IOException {
    	return;
    }
    
    public void decode(FacesContext context) {
    	return;
    }
    

    Sun 的 JSF RI 期望,在組件沒有渲染器的時候,渲染器會發送一個空指針異常。MyFaces 實現不要求處理這個需求,但是在代碼中包含以上方法依然是個好主意,這樣組件既可以在 MyFaces 環境中工作也可以在 JSF RI 環境中工作了。

    MyFaces 更好!

    如果正在使用 Sun JSF RI 或其他替代品,那么請幫自己一個忙,轉到 MyFaces。雖然 MyFaces 不總是 更好的實現,但是目前它是。它的錯誤消息要比 Sun JSF RI 的好,而這個框架相比之下更嚴格。

    第 3 步:創建定制標記

    JSF 組件不是天生綁定到 JSP 上的。要連接起 JSP 世界和 JSF 世界,需要能夠返回組件類型的定制標記(然后在 faces-context 文件中登記)和渲染器,如圖 3 所示。


    圖 3. 連接 JSF 和 JSP
    連接 JSF 和 JSP

    注意,由于沒有獨立的渲染器,所以可以給 getRendererType() 返回 null 值。還請注意,必須已經把 label 屬性的值從定制標記設置到組件上,如下所示:

    
    [LabelTag.java]
    
    public class LabelTag extends UIComponentTag {
    …
    protected void setProperties(UIComponent component) {
    	/* you have to call the super class */
    	super.setProperties(component);
    	((LabelComponent)component).setLabel(label);
    }
    
    

    記住,Tag 設置從 JSP 到 Label 組件的綁定,如圖 4 所示。


    圖 4. 綁定 JSF 和 JSP
    綁定 JSF 和 JSP

    現在要做的全部工作就是創建一個 TLD(標記庫描述符)文件,以登記定制標記,如清單 4 所示:


    清單 4. 登記定制標記
    
    [arcmind.tld]
    
    <taglib>
       <tlib-version>0.03</tlib-version>
       <jsp-version>1.2</jsp-version>
       <short-name>arcmind</short-name>
       <uri>http://arcmind.com/jsf/component/tags</uri>
       <description>ArcMind tags</description>
       
       <tag>
          <name>slabel</name>
          <tag-class>arcmind.simple.LabelTag</tag-class>
          <attribute> 
             <name>label</name> 
             <description>The value of the label</description>
          </attribute> 
       </tag>
    ...
    

    一旦定義了 TLD 文件,就可以開始在 JSP 中使用標記了,如下面示例所示:

    
    [test.jsp]
    <%@ taglib prefix="arcmind" 
             uri="http://arcmind.com/jsf/component/tags" %>
                ...
    	<arcmind:slabel label="Form Test"/>
    

    現在就可以了 —— 開發一個簡單的 JSP 組件不需要更多了。但是如果想創建稍微復雜一些的組件,針對更復雜的使用場景時該怎么辦?請繼續往下看。



    回頁首


    復合組件

    在下一個示例中,我將介紹如何創建這樣一個組件(和標記),它可以記住最后一個人離開的位置。Field 組件把多個組件的工作組合到一個組件中。復合組件是 JSF 組件開發的重點,會節約大量時間!

    Field 組件把標簽、文本輸入和消息功能組合到一個組件。Field 的文本輸入功能允許用戶輸入文本。如果有問題(例如輸入不正確),它的標簽功能會顯示紅色,還會顯示星號(*)表示必需的字段。它的消息功能允許它在必要的時候寫出出錯消息。

    Field 組件示例演示了以下內容:

    • UIInput 組件
    • 處理值綁定和組件屬性
    • 解碼來自請求參數的值
    • 處理出錯消息

    與 Label 組件不同,Field 組件使用獨立渲染器。如果為一個基于 HTML 的應用程序開發組件,那么不要費力使用獨立渲染器。這么做是額外的無用功。如果正在開發許多 JSF 組件,打算賣給客戶,而針對的客戶又不止一個,那么就需要獨立的渲染器了。簡而言之,渲染器適用于商業框架的開發人員,不適用于開發內部 Web 應用程序的應用程序開發人員。

    了解代碼

    由于我已經介紹了創建組件、定義渲染器以及創建定制標記的基本步驟,所以這次我讓代碼自己說話,我只點出幾個重要的細節。在清單 5 中,可以看到在典型的應用程序示例中如何使用 Field 標記的:


    清單 5. Field 標記
    
    <f:view>
      <h2>CD Form</h2>
          
      <h:form id="cdForm">
            
        <h:inputHidden id="rowIndex" value="#{CDManagerBean.rowIndex}" /> 
          
          	
            <arcmind:field id="title"
                               value="#{CDManagerBean.title}"  
                               label="Title:"
                               errorStyleClass="errorText"
                               required="true" /> <br />
    		
            <arcmind:field id="artist"
                               value="#{CDManagerBean.artist}"  
                               label="Artist:"
                               errorStyleClass="errorText"
                               required="true" /> <br />
          	
            <arcmind:field id="price"
                               value="#{CDManagerBean.price}"  
                               label="CD Price:"
                               errorStyleClass="errorText"
                               required="true">
               <f:validateDoubleRange maximum="1000.0" minimum="1.0"/>
            </arcmind:field>
    

    以上標記輸出以下 HTML:

    
    <label style="" class="errorText">Artist*</label>
    <input type="text" id="cdForm:artist " 
           name=" cdForm:artist " />
    Artist is blank, it must contain characters
    

    圖 5 顯示了瀏覽器中這些內容可能顯示的效果。


    圖 5. Field 組件
    Field 組件

    清單 6 顯示了創建 Field 組件的代碼。因為這個組件負責輸入文本而不僅僅是輸出它(像 Label 那樣),所以要從繼承 UIInput 開始,而不是從繼承 UIOutput 開始。


    清單 6. Field 繼承 UIInput
    
    package com.arcmind.jsfquickstart;
    
    import javax.faces.component.UIInput;
    import javax.faces.context.FacesContext;
    
    
    /**
     * @author Richard Hightower
     *  
     */
    public class FieldComponent extends UIInput {
    
    	private String label;
    
        @Override
         public Object saveState(FacesContext context) {
            Object values[] = new Object[2];
            values[0] = super.saveState(context);
            values[1] = label;
            return ((Object) (values));
        }
    
        @Override
        public void restoreState(FacesContext context, Object state) {
            Object values[] = (Object[])state;
            super.restoreState(context, values[0]);
            label = (String)values[1];
        }
        
    	public FieldComponent (){
    		this.setRendererType("arcmind.Field");
    	}
    
    	/**
    	 * @return Returns the label.
    	 */
    	public String getLabel() {
    		return label;
    	}
    
    	/**
    	 * @param label
    	 *  The label to set.
    	 */
    	public void setLabel(String label) {
    		this.label = label;
    	}
    
    	
    	@Override
    	public String getFamily() {
    		return "arcmind.Field";
    	}
    
    
    	public boolean isError() {
    		return !this.isValid();
    	}
    
    }
    

    可以注意到,代表片段中遺漏了編碼方法。這是因為編碼和解碼發生在獨立的渲染器中。我稍后會介紹它。

    值綁定和組件屬性

    雖然 Label 組件只有一個屬性(JSP 屬性),可是 Field 組件卻有多個屬性,即 label、errorStyle、errorStyleClassvaluelabelvalue 屬性位于 Field 組件的核心,而 errorStyleerrorStyleClass 是特定于 HTML 的。因為這些屬性是特定于 HTML 的,所以不需要讓它們作為 Field 組件的屬性;相反,只是把它們作為組件屬性進行傳遞,只有渲染器知道這些屬性。

    像使用 Label 組件時一樣,需要用定制標記把 Field 組件綁定到 JSP,如清單 7 所示:


    清單 7. 為 FieldComponent 創建定制標記
    
    /*
     * Created on Jul 19, 2004
     *
     */
    package com.arcmind.jsfquickstart;
    
    import javax.faces.application.Application;
    import javax.faces.component.UIComponent;
    import javax.faces.context.FacesContext;
    import javax.faces.el.ValueBinding;
    import javax.faces.webapp.UIComponentTag;
    
    
    /**
     * @author Richard Hightower
     *
     */
    public class FieldTag extends UIComponentTag {
    
         private String label;
         private String errorStyleClass="";
         private String errorStyle="";
         private boolean required;
         private String value="";
         
         /**
          * @return Returns the label.
          */
         public String getLabel() {
              return label;
         }
         /**
          * @param label The label to set.
          */
         public void setLabel(String label) {
              this.label = label;
         }
         /**
          * @see javax.faces.webapp.UIComponentTag#setProperties
          * (javax.faces.component.UIComponent)
          */
         @Override
         protected void setProperties(UIComponent component) {
              /* You have to call the super class */
              super.setProperties(component);
              ((FieldComponent)component).setLabel(label);
              component.getAttributes().put("errorStyleClass",
                errorStyleClass);
              component.getAttributes().put("errorStyle",errorStyle);
              ((FieldComponent)component).setRequired(required);
         
         
             FacesContext context = FacesContext.getCurrentInstance();
             Application application = context.getApplication();
             ValueBinding binding = application.createValueBinding(value);
             component.setValueBinding("value", binding);
              
         }
         /**
          * @see javax.faces.webapp.UIComponentTag#getComponentType()
          */
         @Override
         public String getComponentType() {
              return "arcmind.Field";     
         }
    
         /**
          * @see javax.faces.webapp.UIComponentTag#getRendererType()
          */
         @Override
         public String getRendererType() {
              return "arcmind.Field";     
         }
    
         /**
          * @return Returns the errorStyleClass.
          */
         public String getErrorStyleClass() {
              return errorStyleClass;
         }
         /**
          * @param errorStyleClass The errorStyleClass to set.
          */
         public void setErrorStyleClass(String errorStyleClass) {
              this.errorStyleClass = errorStyleClass;
         }
         
         /**
          * @return Returns the errorStyle.
          */
         public String getErrorStyle() {
              return errorStyle;
         }
         /**
          * @param errorStyle The errorStyle to set.
          */
         public void setErrorStyle(String errorStyle) {
              this.errorStyle = errorStyle;
         }
    
         /**
          * @return Returns the required.
          */
         public boolean isRequired() {
              return required;
         }
         /**
          * @param required The required to set.
          */
         public void setRequired(boolean required) {
              this.required = required;
         }
         
         /**
          * @return Returns the value.
          */
         public String getValue() {
              return value;
         }
         /**
          * @param value The value to set.
          */
         public void setValue(String value) {
              this.value = value;
         }
    }
    

    從概念上說,在上面的代碼和 Label 組件之間找不出太大區別。但是,在這個示例中,setProperties 方法有些不同:

    
    protected void setProperties(UIComponent component) {
        /* You have to call the super class */
        super.setProperties(component);
        ((FieldComponent)component).setLabel(label);
        component.getAttributes().put("errorStyleClass", 
          errorStyleClass);
        component.getAttributes().put("errorStyle",errorStyle);
    
        ((FieldComponent)component).setRequired(required);
    

    雖然 label 屬性傳遞時的方式與前面的示例相同,但是 errorStyleClasserrorStyle 屬性不是這樣傳遞的。相反,它們被添加到 JSF 組件的屬性映射 中。Renderer 類會使用屬性映射去渲染類和樣式屬性。這個設置允許特定于 HTML 的代碼從組件脫離。

    這個修訂后的 setProperties 方法實際的值綁定代碼也有些不同,如下所示。

    
    protected void setProperties(UIComponent component) {
          ...	
    	
         FacesContext context = FacesContext.getCurrentInstance();
         Application application = context.getApplication();
         ValueBinding binding = application.createValueBinding(value);
         component.setValueBinding("value", binding);
    

    這個代碼允許 Field 組件的 value 屬性綁定到后臺 bean。出于示例的原因,我把 CDManagerBean 的 title 屬性綁定到 Field 組件,像下面這樣:value="#{CDManagerBean.title}。值綁定是用 Application 對象創建的。Application 對象是創建值綁定的工廠。這個組件擁有保存值綁定的特殊方法,即 setValueBinding;可以有不止一個值綁定。

    獨立渲染器

    最后介紹渲染器,但并不是說它不重要。獨立渲染器必須考慮的主要問題是解碼(輸入) 和編碼(輸出)。Field 組件做的編碼比解碼多得多,所以它的渲染器有許多編碼方法,而只有一個解碼方法。在清單 8 中,可以看到 Field 組件的渲染器:


    清單 8. FieldRenderer 擴展自 Renderer
    
    package com.arcmind.jsfquickstart;
    
    import java.io.IOException;
    import java.util.Iterator;
    import java.util.Map;
    
    import javax.faces.application.FacesMessage;
    import javax.faces.component.UIComponent;
    import javax.faces.component.UIInput;
    import javax.faces.context.FacesContext;
    import javax.faces.context.ResponseWriter;
    import javax.faces.convert.Converter;
    import javax.faces.convert.ConverterException;
    import javax.faces.el.ValueBinding;
    import javax.faces.render.Renderer;
    
    /**
     * @author Richard Hightower
     *
     */
    public class FieldRenderer extends Renderer {
    
    
      @Override 
       public Object getConvertedValue(FacesContext facesContext, UIComponent component, 
         Object submittedValue) throws ConverterException {
            
    
        //Try to find out by value binding
        ValueBinding valueBinding = component.getValueBinding("value");
        if (valueBinding == null) return null;
    
        Class valueType = valueBinding.getType(facesContext);
        if (valueType == null) return null;
    
        if (String.class.equals(valueType)) return submittedValue;    
        if (Object.class.equals(valueType)) return submittedValue;    
    
        Converter converter = ((UIInput) component).getConverter();
        converter =  facesContext.getApplication().createConverter(valueType);
        if (converter != null ) {
            return converter.getAsObject(facesContext, component, (String) submittedValue);
        }else {
            return submittedValue; 
        }
    		
        }
    
       @Override
        public void decode(FacesContext context, UIComponent component) {
            /* Grab the request map from the external context */
           Map requestMap = context.getExternalContext().getRequestParameterMap();
            /* Get client ID, use client ID to grab value from parameters */
           String clientId = component.getClientId(context);
           String value = (String) requestMap.get(clientId);
    		
            FieldComponent fieldComponent = (FieldComponent)component;
              /* Set the submitted value */
            ((UIInput)component).setSubmittedValue(value);
        }
    	
       @Override
        public void encodeBegin(FacesContext context, UIComponent component)
            throws IOException {
            FieldComponent fieldComponent = (FieldComponent) component;
            ResponseWriter writer = context.getResponseWriter();
            encodeLabel(writer,fieldComponent);
            encodeInput(writer,fieldComponent);
            encodeMessage(context, writer, fieldComponent);
            writer.flush();
        }
    
    	
    	
        private void encodeMessage(FacesContext context, ResponseWriter writer, 
          FieldComponent fieldComponent) throws IOException {
            Iterator iter = context.getMessages(fieldComponent.getClientId(context));
            while (iter.hasNext()){
               FacesMessage message = (FacesMessage) iter.next();
               writer.write(message.getDetail());
            }
        }
    
        private void encodeLabel(ResponseWriter writer, FieldComponent 
          fieldComponent) throws IOException{
            writer.startElement("label", fieldComponent);
            if (fieldComponent.isError()) {
                String errorStyleClass = (String) fieldComponent.getAttributes().get("errorStyleClass");
                String errorStyle = (String) fieldComponent.getAttributes().get("errorStyle");
    
                writer.writeAttribute("style", errorStyle, "style");
                writer.writeAttribute("class", errorStyleClass, "class");
            }
            writer.write("" + fieldComponent.getLabel());
            if (fieldComponent.isRequired()) {
                writer.write("*");
            }
           writer.endElement("label");
        }
    	
        private void encodeInput(ResponseWriter writer, FieldComponent 
          fieldComponent) throws IOException{
            FacesContext currentInstance = FacesContext.getCurrentInstance();
            writer.startElement("input", fieldComponent);
            writer.writeAttribute("type", "text", "type");
            writer.writeAttribute("id", fieldComponent.getClientId(currentInstance), "id");
    		writer.writeAttribute("name", fieldComponent.getClientId(currentInstance), "name");
            if(fieldComponent.getValue()!=null)
                writer.writeAttribute("value", fieldComponent.getValue().toString(), "value");
            writer.endElement("input");
        }
    
    }
    

    編碼和解碼

    正如前面提到的,渲染器做的主要工作就是解碼輸入和編碼輸出。我先從解碼開始,因為它是最容易的。 FieldRenderer 的 decode 方法如下所示:

    
    @Override
    public void decode(FacesContext context, UIComponent component) {
           /* Grab the request map from the external context */
         Map requestMap = context.getExternalContext().getRequestParameterMap();
           /* Get client ID, use client ID to grab value from parameters */
         String clientId = component.getClientId(context);
         String value = (String) requestMap.get(clientId);
    		
         FieldComponent fieldComponent = (FieldComponent)component;
           /* Set the submitted value */
         ((UIInput)component).setSubmittedValue(value);
    }
    

    Label 組件不需要進行解碼,因為它是一個 UIOutput 組件。Field 組件是一個 UIInput 組件,這意味著它接受輸入,所以 必須 進行解碼。decode 方法可以從會話、cookie、頭、請求等處讀取值。在大多數請問下,decode 方法只是像上面那樣從請求參數讀取值。Field 渲染器的 decode 方法從組件得到 clientId,以標識要查找的請求參數。給定組件容器的路徑,clientId 被計算成為組件的全限定名稱。而且,因為示例組件在表單中(是個容器),所以它的 clientid 應當是 nameOfForm:nameOfComponent 這樣的,或者是示例中的 cdForm:artist、cdForm:price、cdForm:title。decode 方法的最后一步是把提交的值保存到組件(稍后會轉換并驗證它,請參閱 參考資料 獲取更多關于驗證和轉換的內容)。

    編碼方法沒什么驚訝的。它們與 Label 組件中看到的類似。第一個方法 encodeBegin,委托給三個幫助器方法 encodeLabelencodeInputencodeMessage,如下所示:

    
    @Override
    public void encodeBegin(FacesContext context, UIComponent component)
           throws IOException {
         FieldComponent fieldComponent = (FieldComponent) component;
         ResponseWriter writer = context.getResponseWriter();
         encodeLabel(writer,fieldComponent);
         encodeInput(writer,fieldComponent);
         encodeMessage(context, writer, fieldComponent);
         writer.flush();
    }
    

    encodeLabel 方法負責在出錯的時候,把標簽的顏色改成紅色(或者在樣式表中指定的其他什么顏色),并用星號 (*) 標出必需的字段,如下所示:

    
    private void encodeLabel(ResponseWriter writer, FieldComponent fieldComponent) throws IOException{
         writer.startElement("label", fieldComponent);
         if (fieldComponent.isError()) {
              String errorStyleClass = (String) fieldComponent.getAttributes().get("errorStyleClass");
              String errorStyle = (String) fieldComponent.getAttributes().get("errorStyle");
    
              writer.writeAttribute("style", errorStyle, "style");
              writer.writeAttribute("class", errorStyleClass, "class");
         }
         writer.write("" + fieldComponent.getLabel());
         if (fieldComponent.isRequired()) {
              writer.write("*");
         }
         writer.endElement("label");
    }
    

    首先,encodeLabel 方法檢查是否有錯誤,如果有就輸出 errorStyleerrorStyleClass(更好的版本是只有在它們不為空的時候才輸出 —— 但是我把它留給您做練習?。?。然后幫助器方法會檢查組件是不是必需的字段,如果是,就輸出星號。encodeMessagesencodeInput 方法做的就是這件事,即輸出出錯消息并為 Field 組件生成 HTML 輸入的文本字段。

    注意,神秘方法!

    您可能已經注意到,有一個方法我還沒有介紹。這個方法就是這個類中的“黑馬”方法。如果您閱讀 Renderer(所有渲染器都要擴展的抽象類)的 javadoc,您可能會感覺到這樣的方法是不需要的,現有的就足夠了:這就是我最開始時想的。但是,您和我一樣,都錯了!

    實際上,基類 Renderer 并不 自動調用 Renderer 子類的相關轉換器 —— 即使 Renderer 的 javadoc 和 JSF 規范建議它這樣做,它也沒做。MyFaces 和 JSF RI 擁有為它們的渲染器執行這個魔術的類(特定于它們的實現),但是在核心 JSF API 中并沒有涉及這項功能。

    相反,需要使用方法 getConvertedValues 鎖定相關的轉換器并調用它。清單 9 顯示的方法根據值綁定的類型找到正確的轉換器:


    清單 9. getConvertedValues 方法
    
    @Override
     public Object getConvertedValue(FacesContext facesContext, 
       UIComponent component, Object submittedValue) throws ConverterException {
            
         //Try to find out by value binding
         ValueBinding valueBinding = component.getValueBinding("value");
         if (valueBinding == null) return null;
    
         Class valueType = valueBinding.getType(facesContext);
         if (valueType == null) return null;
    
         if (String.class.equals(valueType)) return submittedValue;    
         if (Object.class.equals(valueType)) return submittedValue;    
    
         Converter converter = ((UIInput) component).getConverter();
         converter =  facesContext.getApplication().createConverter(valueType);
         if (converter != null ) {
              return converter.getAsObject(facesContext, component, (String) submittedValue);
         }else {
              return submittedValue; 
         }
    		
    }
    

    清單 9 的代碼添加了 Render javadoc 和 JSF 規范都讓您相信應當是自動執行的功能,而實際上并不是。另一方面,請注意如果沒有 獨立的 Renderer,就不需要 以上(getConvertedValues)方法。UIComponentBase 類(Field 組件的超類)在直接渲染器的情況下提供了這個功能。請接受我的建議,只在特別想嘗試或者在編寫商業框架的時候,才考慮采用渲染器。在其他情況下,它們不值得額外的付出。

    如果想知道如何把組件和渲染器關聯,那么只要看看圖 6 即可。


    圖 6. 把渲染器映射到組件
    把渲染器映射到組件

    定制標記有兩個方法,分別返回組件類型和渲染器類型。這些方法用于查找配置在 faces-config.xml 中的正確的渲染器和組件。請注意(雖然圖中沒有)組件必須返回正確的 family 類型。



    回頁首


    結束語

    通過這些內容,您已經切實地了解了 JSF 組件開發的核心。當然,在這個領域還有許多其他主題需要涉及 —— 包括發出組件事件、國際化組件、創建 UICommand 樣式的組件,以及更多。請參閱 參考資料 獲得 JSF 的閱讀列表!

    在編寫這篇文章的過程中,我遇到了 Renderer 的一個技術障礙,它使我發現了 getConvertedValues 方法的工作方式。盡管我以前遇到過 Converter 問題并處理過它,但是那時我是在一個緊張的(生產)日程中做這件事的。在生產工作中進行的研究,不必像在 how-to 文章中做得那么詳細;所以這一次,我必須不僅學習如何修補問題,還要學習弄清如何 做對。通過這整個過程,我最終在非常深的層次上學會并體驗了 JSF 組件處理工作的方式。所以,有時繞點彎路會看到優美的風景。

    我希望在這個由四部分組成的系列中,您已經學到了關于使用 JSF 的優勢的充足知識,以及它如何工作的基礎知識,還希望您會喜歡進一步深入這項技術。而且當您有時可能迷失方向的時候,請不要陷入 FUD。相反,請記住我說過的:彎路會看到優美的風景,請繼續前行。

    posted @ 2006-01-10 11:26 lbom 閱讀(576) | 評論 (0)編輯 收藏

    僅列出標題
    共3頁: 上一頁 1 2 3 下一頁 
    主站蜘蛛池模板: 亚洲国产精品久久久久婷婷软件 | 亚洲成AV人片在线观看无| 国产永久免费高清在线| 亚洲一本之道高清乱码| 又粗又硬又黄又爽的免费视频| 免费人成激情视频在线观看冫| 国产AV旡码专区亚洲AV苍井空| 久久久久亚洲精品天堂久久久久久 | 亚洲成A∨人片在线观看不卡| 国拍在线精品视频免费观看 | 久草免费福利在线| 亚洲伊人色一综合网| 丝袜熟女国偷自产中文字幕亚洲| 日本免费xxxx| 光棍天堂免费手机观看在线观看| 亚洲情A成黄在线观看动漫软件| 中文字幕精品亚洲无线码二区| 中文字幕影片免费在线观看 | 亚洲日韩中文在线精品第一| 91精品国产免费久久久久久青草| 国产成人精品免费视频大全| 亚洲久悠悠色悠在线播放| 亚洲乱码中文字幕久久孕妇黑人| 毛片基地免费视频a| 免费国产99久久久香蕉| 黄床大片30分钟免费看| 久久精品国产亚洲av麻豆蜜芽| 亚洲精品国产品国语在线| 蜜臀91精品国产免费观看| 在线免费观看亚洲| 成人免费乱码大片A毛片| 精品亚洲成a人在线观看| 亚洲卡一卡2卡三卡4麻豆| 亚洲AV无码日韩AV无码导航| 无码国产亚洲日韩国精品视频一区二区三区 | 亚洲va久久久噜噜噜久久天堂| 国产麻豆剧传媒精品国产免费| 国产91免费视频| 222www免费视频| 99热在线精品免费播放6| a级日本高清免费看|