Tapestry

          記錄學習Tapestry專用布格格。很多文章都轉載網絡。

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            20 隨筆 :: 0 文章 :: 4 評論 :: 0 Trackbacks

          tapestry的URL形如/examples/app?service=page/Admin
          能 夠保證有效運行的一個非常重要的原因是,用有狀態的javabean代替無狀態的servlet構建一個tapestry應用。page是有狀態的,他只 能在一個線程里為一個用戶處理一個request,而一個servelet,沒有用戶的狀態,可以在并發線程中為任何數量同時發生的request提供服 務。使用有狀態的page遇到的問題和使用數據庫的連接遇到的問題非常相似。 

          engine
          engine 是每個tapestry應用的中心,它是一個負責支持和組織應用所有方面的對象,他把所有小的子系統綁在一起構成一個tapestry應用,它首先負責管 理server端的狀態,管理Visit對象及持久頁面屬性,他會被保存在session中。engine對象的service()方法,負責進來的 request處理和把響應返回給客戶端。 ApplicationServlet調用public boolean service(RequestContext context)方法figure 7.4,執行request處理,此服務不僅要進行很多的初始化工作,更重要的是它包括多級的異常捕捉、報告,任何未捕捉的異常會由異常頁來呈現。過程如 下:
          1).調用AbstractEnginer的protected void setupForRequest(RequestContext context)方法,確保engine對象被設置,這個方法很重要,細節可參看API文檔,在覆寫類方法的子類中,必須首先第一句調用這個方法
          2).調用自己的getService(String name)
          3).new RequestCycle(IEngine engine, RequestContext requestContext, IEngineService service, IMonitor monitor)
          4).調用IEngineService的service(IEngineServiceView engine, IRequestCycle cycle, ResponseOutputStream output)方法
          5).調用RequestCycle對象的cleanup()
          6).調用自己的cleanupAfterRequest(IRequestCycle cycle)方法

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">


          Engine service

          Engine service是實現了IEngineService接口的對象,他包含很多創建和服務應用URL的方法,且全是在一個對象中,Engine service更象servlet,他能被很多線程共享,不能記錄客戶狀態。tapeestry默認是有9個service,4個最長用的是home, page,direct和external,可見Table 7.3,大部分service有相對應的部件
          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">

          IEngineService 的service(IEngineServiceView engine, IRequestCycle cycle, ResponseOutputStream output)方法,IRequestCycle的一些方法調用等,各個service的調用是不相同,這些步之后各個service都要回調 IEngine對象的renderResponse()方法,處理也是一樣的。

          home Service:
          1).調用IRequestCycle的getPage(String name),返回home page
          2). 調用IRequestCycle的activate(IPage page)方法,此方法為request設置最終返回客戶端顯示的活動頁面,活動頁面典型的由service設置,但因為可能被替換要顯示的頁面,也會經 常被validator方法pageValidate(PageEvent event)改變,這個方法的操作過程如下:它調用page對象的validate(IRequestCycle cycle)方法,(validate()方法用于基本的安全驗證,這個方法實際上并不執行任何檢查Figure7.06,page對象可以有多個 PageValidateListener,The validate()方法調用每個validator對象的pageValidate()。最通常的方法是page對象自己實現 PageValidateListener接口,會自動注冊成為自己的validator。validator可以通過throw a PageRedirectException激活不同的頁面,當PageRedirectException異常被扔出,由service處理的 request過程將被中止,被異常指定的頁面被激活并被立馬呈現給客戶端。)
          3).service對象回調engine的renderResponse()方法,將使活動頁面被呈現并響應給客戶端

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">

           

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">


          page service:
          除了service對象和home service不一樣,其他步驟相同

          direct service:
          DirectLink 和Form部件使用這個服務,這兩個部件都實現IDirect接口,當處理form的submit時,會首先執行一個rewind動作,之后執行form 指定的listener動作。direct service能夠檢查session是否過期,DirectLink和Form部件的“stateful”屬性,默認為“false”,設置為 “true”就可以進行session檢查,當呈現響應時,direct service會生成URL,形如:/examples/app?service=direct/1/Guess/select,URL中的1,就標明這 需要檢查session是否過期,

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">
          一旦session過期,用戶就會看到Session已經過期的頁面,默認的是個很簡陋的,可以創建一個命名為“StaleSession”的page,來給用戶提供一個更友好的界面。處理請求,DirectService的service()方法處理過程:
          1).調用IRequestCycle的getPage(String name),返回page
          2). 調用IRequestCycle的activate(IPage page)方法,此方法為request設置最終返回客戶端顯示的活動頁面,活動頁面典型的由service設置,但因為可能被替換要顯示的頁面,也會經 常被validator方法pageValidate(PageEvent event)改變,這個方法的操作過程如下:它調用page對象的validate(IRequestCycle cycle)方法,(validate()方法用于基本的安全驗證,這個方法實際上并不執行任何檢查,page對象可以有多個 PageValidateListener,The validate()方法調用每個validator對象的pageValidate()。最通常的方法是page對象自己實現 PageValidateListener接口,會自動注冊成為自己的validator。validator可以通過throw a PageRedirectException激活不同的頁面,當PageRedirectException異常被扔出,由service處理的 request過程將被中止,被異常指定的頁面被激活并被立馬呈現給客戶端。)
          3).調用IPage的getNestedComponent(String path),返回一個IDirect對象
          4).調用IDirect對象的isStateful(),如果為true,session過期檢查將要發生,檢查HttpSession過期,StaleSessionException異常將被服務扔出
          5).調用IRequestCycle的setServiceParameters(Object[] parameters),由service調用,service參數被解開并存入request cycle的serviceParameters屬性中
          6).調用IDirect對象的trigger(IRequestCycle cycle),調用部件的listener方法,執行相應的action
          7).service對象回調engine的renderResponse()方法,將使活動頁面被呈現并響應給客戶端
          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">

          需要注意的是,各方法調用順序很重要,validate()發生的比較早,那時還不能訪問service參數,session檢查發生在validate()之后

          對于DirectLink部件,trigger()方法內部處理過程:
          1).調用IActionListenerr的actionTriggered(IComponent component,IRequestCycle cycle)方法
          1.1).通過反射機制調用listener方法

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">

          對于Form部件,trigger()方法執行,要執行rewind,內部過程Figure 7.12:
          1).調用IRequestCycle的rewindForm(IForm form,String targetActionId)方法
          1.1).調用page對象的beginPageRender(),觸發適當的事件
          1.2).回調IForm對象的rewind(IMarkupWriter writer,IRequestCycle cycle)
          1.2.1).調用IForm對象的render(IMarkupWriter writer, IRequestCycle cycle)
          1.2.2).調用監聽方法等
          1.3).調用page對象的endPageRender(),觸發適當的事件

           

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">

          external Service:
          1).調用IRequestCycle的getPage(String name),返回page
          2).調用IRequestCycle的setServiceParameters(Object[] parameters),由service調用
          3). 調用IRequestCycle的activate(IPage page)方法,此方法為request設置最終返回客戶端顯示的活動頁面,活動頁面典型的由service設置,但因為可能被替換要顯示的頁面,也會經 常被validator方法pageValidate(PageEvent event)改變,這個方法的操作過程如下:它調用page對象的validate(IRequestCycle cycle)方法,(validate()方法用于基本的安全驗證,這個方法實際上并不執行任何檢查,page對象可以有多個 PageValidateListener,The validate()方法調用每個validator對象的pageValidate()。最通常的方法是page對象自己實現 PageValidateListener接口,會自動注冊成為自己的validator。validator可以通過throw a PageRedirectException激活不同的頁面,當PageRedirectException異常被扔出,由service處理的 request過程將被中止,被異常指定的頁面被激活并被立馬呈現給客戶端。)
          3).調用IExternalPage的activateExternalPage(Object[] parameters, IRequestCycle cycle)
          4).service對象回調engine的renderResponse()方法,將使活動頁面被呈現并響應給客戶端
          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">


          從池中獲得一個頁面
          雖然采用池化技術,因為page眾多,我認為,在最開始池中是沒有page的,只有第一次訪問生成一個完整page,直接返回給request使用,使用完畢page返回池中等待復用。
          Figure 7.19 IRequestCycle的getPage(String name)方法返回一個page實例,這個實例被request cycle對象在整個request期間緩存,將來調用同名page名字的getPage()會返回相同實例,getPage()方法內部步驟是,
          1). 用IPageSource的getPage(IRequestCycle cycle, String pageName, IMonitor monitor)方法,IPageSource就是page池,他能實例化一個新的page實例,如果池中沒有可用的page實例,page實例調用自己 的attach(IEngine value)以把自己綁定到一個具體的engineh上,一直到request cycle結束,才解除綁定返回池中。
          2).調用page的setRequestCycle(IRequestCycle cycle)
          3). 調用IEngine的getPageRecorder(String pageName,IRequestCycle cycle),IPageRecorder是一個對象,負責追蹤page的持久頁面屬性變化的,當持久頁面屬性改變了,新值就會被記錄在 HttpSession中。page持久狀態是特定于一個專門用戶,完全和page實例分開的。IPageRecorder通過簡單的通知機制被鉤入 page實例,IPageRecorder觀察到持久屬性改變,就會他作為一個命名的session屬性把持久屬性值安全的保存到 HttpSession,各自的持久頁面屬性被作為獨自的HttpSession屬性保存
          4).調用IPageRecorder的rollback(IPage page),將頁面持久屬性恢復到HttpSession屬性保存的值

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">

          創建一個新Page實例
          當請求page,而池中又沒有實例可用,page source會利用PageLoader(是類不是IPageLoader接口)來創建一個page實例figure 7.20:
          PageLoader不是threadsafe,PageSource要創建一個新的PageLoader實例為每一個要裝載的頁面,為解決多線程的問題
          PageLoader的IPage loadPage(String name,
                                INamespace namespace,
                                IRequestCycle cycle,
                                IComponentSpecification specification)過程:
          1).new一個page實例,java page class被實例化
          2).初始化屬性,page的初始屬性被設置,包括page名字
          3).page實例調用自己的attach(IEngine value)以把自己綁定到一個具體的engineh上
          4).page包含的部件被遞歸創建,每一個部件被創建,page loader就會調用部件的finishLoad()方法,從BaseComponent類繼承的部件在這時也會裝載他的模板
          5).page的finishLoad()方法被調用

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">
          一 旦page的finishLoad()方法執行完畢,一個初始化和配置過的完整的page對象被返回給request cycle。對于頁面和部件來說,當在page or component specification中對有的初始化不能被表達時,重載后的finishLoad()方法是個進行這種最終初始化的好地方,經常這類初始化要涉及 page中的其他部件。finishLoad()有兩個方法,public void finishLoad(
            IRequestCycle cycle,
            IPageLoader loader,
            IComponentSpecification specification);
          protected void finishLoad();
          盡 量重載protected的無參的finishLoad(),且無需先調用父類的方法,除非要使用到3個參數,才會重載public的,且當重載 public的帶3個參數的方法時,必須先調用父類的public的同名方法,一旦調用失敗,就會造成加載page或component的模板失敗,也會 造成其他方面的影響

          把Page實例返回池
          在request 結束時,response被發送回client之后,附著在request上的page必須被返回池,頁面屬性包括持久的臨時的都必須被重置回初始值,以 供其他用戶的request使用。如果保留屬性值不重置是非常危險的,因為所有的page實例是完全共享的,另一個request完全有可能從池中獲得上 一個用戶使用過的page,所以信息就會暴露。IRequestCycle的cleanup()方法Figure 7.21,會釋放所有他擁有的資源,就包括釋放page回page source,過程如下:
          1).調用IPageSource的releasePage(IPage page)方法
          1.1).IPage的detach()方法
          1.1.1).清除changeObserved屬性,Clears the changeObserved property
          1.1.2).調用所有注冊的相關監聽器的PageDetachListener.pageDetached(PageEvent)
          1.1.3).調用org.apache.tapestry.AbstractPage.initialize()清除重置所有屬性
          1.1.4).Clears the engine, visit and requestCycle properties,the page's visit,engine,and requestCycle properties are reset to null.

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">
          子類可以覆寫這個方法,但是必須在子類方法中調用父類的同名方法,通常是在方法的最后(以上detach()說明主要參見AbstractPage.detach()方法API文檔)
          實際上只要你用<property-specification>元素來聲明持久或臨時屬性,就不必關心頁面的清除工作, detach()或initialize()方法主要是早期版本中要顧及的。

          頁面呈現過程:
          tapestry呈現的核心是IRender接口,只有一個方法:
          public void render(IMarkupWriter writer, IRequestCycle cycle);
          這 個接口被希望參與頁面呈現處理的所有對象所實現,他是IComponent的父接口,因此所有的部件都可以被呈現。IMarkupWriter接口一個很 重要的工作,把所有xml保留字自動轉換,如><等,不用程序來干預。BasePage類實現了 getResponseWriter(),源碼為:
          public IMarkupWriter getResponseWriter(OutputStream out) {
                      return new HTMLWriter(out, getOutputEncoding());
          }
          HTMLWriter的getContentType()方法默認會返回一個字符串“text/html; charset=UTF-8”,因此若要返回xml類型,應該在BasePage的子類中覆寫getResponseWriter()方法,如下:
          public IMarkupWriter getResponseWriter(OutputStream out) {
                      return new HTMLWriter("text/xml",getOutputEncoding(),out);
          }
          若要返回wml類型,應該在BasePage的子類中覆寫getResponseWriter()方法,如下:
          public IMarkupWriter getResponseWriter(OutputStream out) {
                      return new WMLWriter(out, getOutputEncoding());
          }

          engine對象的renderResponse()方法:
          1).調用IRequestCycle對象的getPage(),得到page對象
          2).調用page對象的public IMarkupWriter getResponseWriter(OutputStream out)方法,返回一個IMarkupWriter對象
          3).調用IMarkupWriter對象的getContentType(),這個值用來設置HttpServletResponse的setContentType()方法
          4). 調用IRequestCycle對象的renderPage(IMarkupWriter writer),呈現指定的頁面,應用應該總是用這個方法來呈現頁面,而不是直接調用IRender.render(IMarkupWriter, IRequestCycle),因為在呈現之前cycle對象必須進行一些設置;
          4.1).IRequestCycle對象的 renderPage()調用page對象renderPage(IMarkupWriter writer,IRequestCycle cycle),被調用來呈現完整頁面,這個方法應該只由IRequestCycle.renderPage(IMarkupWriter writer)調用,這個方法內執行呈現的具體過程如下Figure 7.18
          4.1.1).調用PageRenderListener的pageBeginRender(org.apache.tapestry.event.PageEvent)方法
          4.1.2).調用page對象的beginResponse(IMarkupWriter, IRequestCycle),這是最后一次機會可以修改持久屬性
          4.1.1).回調IRequestCycle的commitPageChanges()方法,這個方法會通知負責持久屬性管理的page recorders進行相應的保存
          4.1.1).調用page對象的render(IMarkupWriter, IRequestCycle),page開始呈現他的模板的內容,同樣的遞歸呈現他包含的部件
          4.1.1). 調用PageRenderListener的pageEndRender(org.apache.tapestry.event.PageEvent) (this occurs even if a previous step throws an exception).

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">
          一旦呈現頁面開始,持久頁面屬性就不能再被修改!!!

          頁面屬性
          聲 明的屬性,持久的、臨時的都可以有一個初始值,初始值或者是<property-specification>元素的"initial- value"屬性的值,或者是<property-specification>元素體的內容,初始值是OGNL表達式,此表達式只被計算一 次并且表達式的值被保存起來,是在page或component的finishLoad()方法被調用之后。表達式的值被用來給屬性賦初值,當page被 從request拆開時,為復用返回page池之前,此表達式的值被用來更新屬性。初始值也可以不在<property- specification>元素指定,可以在finishLoad()方法內設置,finishLoad()方法調用完畢,tapestry框架 會讀這個屬性,讀出的值就將是屬性的初值,會被保存為以后用,當page拆開時返回前,此保存的初值會被重新賦給屬性,也就是說,無論是在initial -value或者是在finishLoad()中,都可指定初值,然后此初值被tapestry保存用于為屬性恢復初值!

          0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" alt="" border="0">
           
          posted on 2007-08-03 17:39 Tapestry 閱讀(1100) 評論(0)  編輯  收藏 所屬分類: Tapestry
          主站蜘蛛池模板: 博兴县| 大荔县| 卢龙县| 和林格尔县| 梅州市| 郎溪县| 延安市| 都安| 蓬莱市| 石柱| 西乌珠穆沁旗| 平原县| 堆龙德庆县| 钟祥市| 双柏县| 延长县| 天柱县| 江口县| 遂川县| 沂南县| 师宗县| 龙川县| 绩溪县| 香港 | 花垣县| 建湖县| 葫芦岛市| 高雄县| 合水县| 蕉岭县| 健康| 洛川县| 遂昌县| 黄龙县| 长岭县| 安仁县| 乡宁县| 景德镇市| 绥宁县| 张掖市| 青龙|