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

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

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

    狂奔 lion

    自強不息

    2007年5月31日

    淺談Java中的同步的方法和原理

    Java的內存模型中Thread會附有自己的堆棧,寄存器,必要時需要和主存即heap之間同步。
    可以使用Synchornized關鍵字和Concurrent包中的Lock可以保證線程互斥和可見性。

    互斥性體現在類鎖或者對象鎖上,每個對象自身都包含一個監視器,該監視器是一個每次只能被一個線程所獲取進入的臨界區,可以通過wait和notify來退出和準入臨界區??梢?/font>看出這是一個生產者-消費者的模型。而Concurrent包中的Lock為了能夠獲得更好的性能和更好的擴展性,以及不依賴于關鍵字的可讀代碼,自己實現了這樣一個生產消費隊列,也就是AbstractQueuedSynchronizer,被稱為AQS的機制。每個Lock都內置了一個AbstractQueuedSynchronizer。需要說明的是AbstractQueuedSynchronizer內部實現采用了CAS機制,通過getState, setState, compareAndSetState訪問控制一個32bit int的形式進行互斥。

    那么可見性是如何保證的呢?

    對于關鍵字的同步機制,其實可見性就是線程和主存之間的同步時機問題。共有4個時間點需要注意:
    1 獲取或釋放類鎖/對象鎖的時候。Thread保證reload/flush全部變更
    2 volatile就是flush on write或者reload on read
    3 當線程首次訪問共享變量時,可以得到最新的結果。
    題外:所以在構造方法中公布this時很危險的。簡單的說,就是構造時不逃脫任何變量,不開啟新的線程,只做封裝。關于安全構造,請參考
    http://www.ibm.com/developerworks/cn/java/j-jtp0618/#resources
    4 線程結束時,所有變更會寫回主存

    關于Concurrent Lock如何實現可見性的問題,Doug Lea大俠,只在他的論文中提到,按照JSR133,Unsafe在getState, setState, compareAndSetState時保證了線程的變量的可見性,不需要額外的volatile支持,至于具體這些native做了哪些magic就不得而知了,總之,最后的contract就是保證lock區間的共享變量可見性。開發團隊被逼急了就這樣回答:
    There seems to be a real reluctance to explain the dirty details. I think the question was definitely understood on the concurrent interest thread, and the answer is that synchronized and concurrent locking are intended to be interchangable in terms of memory semantics when implemented correctly. The answer to matfud's question seems to be "trust us.”

    不過這個地方的確是開發團隊給我們用戶迷惑的地方,在同樣應用了CAS機制的Atomic類中,都內嵌了volatile變量,但是再lock塊中,他告訴我們可以保證可見性。

    感興趣的同學可以下面的兩個thread和Doug Lea的thesis:
    http://altair.cs.oswego.edu/pipermail/concurrency-interest/2005-June/001587.html
    http://forums.sun.com/thread.jspa?threadID=631014&start=15&tstart=0
    http://gee.cs.oswego.edu/dl/papers/aqs.pdf

    posted @ 2010-07-09 19:49 楊一 閱讀(1859) | 評論 (0)編輯 收藏

    commons-net FTPClient API存取設計

    文件系統無非就是文件的存取和組織結構。
    訪問一個文件系統的API也應該是寫,讀,定位方法(Pathname?/URI?)

    FTPClient針對文件的保存和獲取各提供了兩個方法,分別是:

    public boolean storeFile(String remote, InputStream local)
    public OutputStream storeFileStream(String remote)

    public boolean retrieveFile(String remote, OutputStream local)
    public InputStream retrieveFileStream(String remote)

     

    兩個方法貌似相同,實際不同,返回流的那個因為不能馬上處理流,所以需要用戶手工調用completePendingCommand,而另一個傳遞流進去的則不需要??赡苡型瑢W已經遇到過這個問題了,讀寫第一個文件時總是正確的,當相同API讀寫第二個文件時,block住了。這是因為FTPClient要求在進行流操作之后執行completePendingCommand,以確保流處理完畢,因為流處理不是即時的,所以也沒有辦法不手工調用completePendingCommand。問題是開發者把不返回流的方法末尾加上了completePendingCommand,如果不看代碼可能根本不知道。
    文檔上說:

         * There are a few FTPClient methods that do not complete the
         
    * entire sequence of FTP commands to complete a transaction.  These
         
    * commands require some action by the programmer after the reception
         
    * of a positive intermediate command.  After the programmer's code
         * completes its actions, it must call this method to receive
         
    * the completion reply from the server and verify the success of the
         
    * entire transaction.


    但是這樣仍然還是讓人有點困惑,為什么都是存儲/讀取的方法,有時候要調用completePendingCommand,有時候不調用?更嚴重的問題是completePendingCommand調用了getReply,如果一個命令通過socket stream傳了過去但是沒有getReply,即沒有completePendingCommand,那么下次發命令時,將會受到本次返回碼的干擾,得到無效的響應。而如果在completePendingCommand之后又進行了一次無辜的completePendingCommand,那么因為FTP Server上沒有Reply了,就會block。所以completePendingCommand并不是可以隨意添加的。

    現在出現了兩個問題:
    1 completePendingCommand很容易多出來或遺漏
    2 顯式調用completePendingCommand暴露了底層實現,給用戶帶來不便,用戶只想要InputStream或者OutputStream

    為了解決這個問題,可以對InputStream進行擴展,建立一個ReplyOnCloseInputStream,如下:

    private static ReplyOnCloseInputStream extends InputStream{
      
    //
      public ReplyOnCloseInputStream(InputStream is, FTPClient c){
        
    //
      }

      
    //
      @override
      
    public void close(){
        
    if(c.completePendingCommand){
          is.close();
        }
    else{
          
    //throw Exception
        }

      }

    }
     
    //
    return new ReplyOnCloseInputStream(is, client);


    這樣封裝之后,FTPClient的用戶只需要正常在處理完流之后關閉即可,而不必暴露實現細節。保存文件也可以用相同的方法封裝OutputStream。

    posted @ 2010-07-07 23:08 楊一 閱讀(3464) | 評論 (1)編輯 收藏

    關于ThreadLocal的內存泄露

    ThreadLocal是一種confinement,confinement和local及immutable都是線程安全的(如果JVM可信的話)。因為對每個線程和value之間存在hash表,而線程數量未知,從表象來看ThreadLocal會存在內存泄露,讀了代碼,發現實際上也可能會內存泄露。

    事實上每個Thread實例都具備一個ThreadLocal的map,以ThreadLocal Instance為key,以綁定的Object為Value。而這個map不是普通的map,它是在ThreadLocal中定義的,它和普通map的最大區別就是它的Entry是針對ThreadLocal弱引用的,即當外部ThreadLocal引用為空時,map就可以把ThreadLocal交給GC回收,從而得到一個null的key。

    這個threadlocal內部的map在Thread實例內部維護了ThreadLocal Instance和bind value之間的關系,這個map有threshold,當超過threshold時,map會首先檢查內部的ThreadLocal(前文說過,map是弱引用可以釋放)是否為null,如果存在null,那么釋放引用給gc,這樣保留了位置給新的線程。如果不存在slate threadlocal,那么double threshold。除此之外,還有兩個機會釋放掉已經廢棄的threadlocal占用的內存,一是當hash算法得到的table index剛好是一個null key的threadlocal時,直接用新的threadlocal替換掉已經廢棄的。另外每次在map中新建一個entry時(即沒有和用過的或未清理的entry命中時),會調用cleanSomeSlots來遍歷清理空間。此外,當Thread本身銷毀時,這個map也一定被銷毀了(map在Thread之內),這樣內部所有綁定到該線程的ThreadLocal的Object Value因為沒有引用繼續保持,所以被銷毀。

    從上可以看出Java已經充分考慮了時間和空間的權衡,但是因為置為null的threadlocal對應的Object Value無法及時回收。map只有到達threshold時或添加entry時才做檢查,不似gc是定時檢查,不過我們可以手工輪詢檢查,顯式調用map的remove方法,及時的清理廢棄的threadlocal內存。需要說明的是,只要不往不用的threadlocal中放入大量數據,問題不大,畢竟還有回收的機制。

    綜上,廢棄threadlocal占用的內存會在3中情況下清理:
    1 thread結束,那么與之相關的threadlocal value會被清理
    2 GC后,thread.threadlocals(map) threshold超過最大值時,會清理
    3 GC后,thread.threadlocals(map) 添加新的Entry時,hash算法沒有命中既有Entry時,會清理

    那么何時會“內存泄露”?當Thread長時間不結束,存在大量廢棄的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一個廢棄ThreadLocal在map中命中)時。

    posted @ 2010-07-02 18:27 楊一 閱讀(2281) | 評論 (2)編輯 收藏

    關于軟件文檔,我的看法

    文檔應該包括兩大部分,一部分是清晰的代碼結構和注釋,比如Concurrent API就是這樣,還有一部分是文字文檔,包括三個小部分:一是開發文檔,應該講架構和功能;二是索引文檔,詳細介紹功能和參數,三是用戶文檔,包括安裝和使用說明

    文檔最困難的莫過于版本的一致性,當軟件升級后,一些obsolete的內容和新的feature很難同步。要是架構發生了變化,那就更困難了。一般document team都不是太精于技術,所以也會產生一些問題。

    只能說任何事物永遠都有改進的空間,但是同樣也永遠沒有達到完美的程度

    posted @ 2010-06-29 18:26 楊一 閱讀(320) | 評論 (0)編輯 收藏

    NIO學習之Web服務器示例

         摘要: 1 根據cpu core數量確定selector數量 2 用一個selector服務accept,其他selector按照core-1分配線程數運行 3 accept selector作為生產者把獲得的請求放入隊列 4 某個selector作為消費者從blocking queue中取出請求socket channel,并向自己注冊 5 當獲得read信號時,selector建立工作...  閱讀全文

    posted @ 2010-06-25 19:19 楊一 閱讀(1967) | 評論 (0)編輯 收藏

    多線程的知識

    多線程的優點:
    1 多核利用
    2 為單個任務建模方便
    3 異步處理不同事件,不必盲等
    4 現代的UI也需要它
    風險:
    1 同步變量易錯誤
    2 因資源限制導致線程活躍性問題
    3 因2導致的性能問題
    用途:
    框架,UI,Backend
    線程安全的本質是什么:
    并非是線程和鎖,這些只是基礎結構,本質是如何控制共享變量訪問的狀態
    什么是線程安全:
    就是線程之間的執行還沒有發生錯誤,就是沒有發生意外
    一個線程安全的類本身封裝了對類內部方法和變量的異步請求,調用方無需考慮線程安全問題
    無狀態的變量總是線程安全的
    原子性:
    完整執行的單元,如不加鎖控制,則會發生競態條件,如不加鎖的懶漢單例模式,或者復合操作。
    鎖,內在鎖,重入:
    利用synchronized關鍵字控制訪問單元,同一線程可以重入鎖內部,避免了面向對象產生的問題。同一變量的所有出現場合應該使用同一個鎖來控制。synchronized(lock)。
    即使所有方法都用synchronized控制也不能保證線程安全,它可能在調用時編程復合操作。
    活躍性和性能問題:
    過大的粒度會導致這個問題,用鎖進行異步控制,導致了線程的順序執行。
    簡單和性能是一對矛盾,需要適當的取舍。不能在沒有考慮成熟的情況下,為了性能去犧牲簡潔性。
    要盡量避免耗時操作,IO和網絡操作中使用鎖

    posted @ 2010-06-25 19:17 楊一 閱讀(375) | 評論 (0)編輯 收藏

    Ext Store Filter的實現和問題

    Store包含兩個數據緩存 - snapshot和data,grid,combo等控件的顯示全部基于data,而snapshot是數據的完整緩存,當首次應用過濾器時,snapshot從data中備份數據,當應用過濾器時,filter從snapshot獲取一份完整的數據,并在其中進行過濾,過濾后的結果形成了data并傳遞給展示,及data總是過濾后的數據,而snapshot總是完整的數據,不過看名字讓人誤以為它們的作用正好相反。
    相應地,當進行store的增刪改時,要同時維護兩個緩存。
    問題
    Store包含兩個增加Record的方法,即insert和add,其中的insert沒有更新snapshot所以當重新應用filter時,即data被重新定義時,在data中使用insert新增的記錄是無效的。
    解決方法
    用add不要用insert,如果用insert,記得把數據寫進snapshot: store.snapshot.addAll(records)

    posted @ 2010-06-25 19:16 楊一 閱讀(1284) | 評論 (0)編輯 收藏

    Ext中Combo組件的聯動封裝

         摘要: 在Extjs中構造N級聯動下拉的麻煩不少,需定制下拉數據并設定響應事件。通過對Combo集合的封裝,無需自己配置Combo,只需設定數據和關聯層級,即可自動構造出一組支持正向和逆向過濾的聯動下拉并獲取其中某一個的實例。 如: 數據: Ext.test = {};       Ext.test.lcbdata&nb...  閱讀全文

    posted @ 2010-06-25 19:14 楊一 閱讀(1306) | 評論 (0)編輯 收藏

    前端框架動態組件和代碼生成之間的選擇

    目前主流的SSH開發架構中,為減輕開發者工作,便于管理開發過程,往往用到一些公共代碼和組件,或者采用了基于模版的代碼生成機制,對于后臺的DAO,Service等因為架構決定,代碼生成必不可少,但是在前端頁面的實現上,卻可以有兩種不同的思路,一種是把配置信息直接封裝成更高級別的組建,一種是進行代碼生成。請大家討論一下這兩種方案的優劣,這里先拋磚引玉了。

    相同點:
    配置信息:XML OR 數據庫

    控件化:
    優點:
    1 易于添加公共功能
    2 修改配置數據直接生效
    3 代碼結構清晰,對開發者友好
    缺點:
    1 重組內存中對象結構,性能沒有代碼生成好(但渲染時間相同)
    2 僅能控制組件自身封裝的配置,不支持個性化修改,如果配置文件不支持的參數,則控件不支持
    3 必須保證每個控件一個配置

    代碼生成:
    優點:
    1 性能較好
    2 易于定制內容
    3 可以只配置一個模版,然后做出多個簡單的修改
    缺點:
    1 不能針對多個頁面同時添加公共功能
    2 業務修改需要重新生成代碼
    3 開發者需要修改自動生成的代碼,并需要了解一些底層的實現結構

    =====================20091029
    代碼生成并不能提高工作效率,尤其是針對復雜的富客戶端開發
    開發組件可提提供一種有效的選項,但是在運行效率和內存處理上需要細心處理

    posted @ 2010-06-25 19:11 楊一 閱讀(453) | 評論 (0)編輯 收藏

    Javascript工作流引擎代碼及實例

     

    最近在學習jBPMJavascript,所以按照一些相關概念自己寫了下面的200行代碼的“工作流引擎”,工作流管理系統包含了流程定義,引擎,及應用系統三個主要部分,下面的代碼實現了流程的分支合并,目前只支持一種環節上的遷移。拷貝到html,雙擊就可以跑起來。

     

    var workflowDef = {
             start:{
                       fn:
    "begin"//對應處理方法可以在內部定義,也可以在外部定義
                       next:[
    "task1","task2"]
             },
             end:
    "end",
             tasks:[{
                       id:
    "task1",
                       fn:
    function(){
                                alert(
    "執行任務一");
                       },
                       before:
    function(){
                                alert(
    "執行任務一前");
                       },
                       after:
    function(){
                                alert(
    "執行任務一后");
                       },
                       next:[
    "task4","task5"]
             },{
                       id:
    "task2",
                       fn:
    function(){
                                alert(
    "執行任務二");
                       },
                       before:
    function(){
                                alert(
    "執行任務二前");
                       },
                       after:
    function(){
                                alert(
    "執行任務二后");
                       },
                       next:[
    "task3"]
             },{
                       id:
    "task3",
                       fn:
    function(){
                                alert(
    "執行任務三");
                       },
                       before:
    function(){
                                alert(
    "執行任務三前");
                       },
                       after:
    function(){
                                alert(
    "執行任務三后");
                       },
                       
    //定義合并的數量
                       merge: 
    3,
                       next:
    "EOWF"
             },{
                       id:
    "task4",
                       fn:
    function(){
                                alert(
    "執行任務四");
                       },
                       before:
    function(){
                                alert(
    "執行任務四前");
                       },
                       after:
    function(){
                                alert(
    "執行任務四后");
                       },
                       next:[
    "task3"]
             },{
                       id:
    "task5",
                       fn:
    function(){
                                alert(
    "執行任務五");
                       },
                       before:
    function(){
                                alert(
    "執行任務五前");
                       },
                       after:
    function(){
                                alert(
    "執行任務五后");
                       },
                       next:[
    "task3"]
             }]
    }

     

     

    //////////定義引擎////////////

    Yi 
    = {};
    Yi.Utils 
    = {};
    Yi.Utils.execute 
    = function(o){
             
    if(typeof o != 'function')
                       eval(o)();
             
    else
                       o();
    }
    //工作流類
    Yi.Workflow 
    = function(workflowDef){
             
    this.def = workflowDef;
             
    this.tasks = this.def.tasks;
    }
    //public按照環節id查找查找
    Yi.Workflow.prototype.findTask 
    = function(taskId){
             
    for(var i=0;i<this.tasks.length;i++){
                       
    if(this.tasks[i].id == taskId)
                                
    return this.tasks[i];
             }
    }
    //public啟動工作流
    Yi.Workflow.prototype.start 
    = function(){
             
    this.currentTasks = [];
             Yi.Utils.execute(
    this.def.start.fn);
             
    for(var i=0;i<this.def.start.next.length;i++){
                       
    this.currentTasks[i] = this.findTask(this.def.start.next[i]);
                       Yi.Utils.execute(
    this.currentTasks[i].before);
             }
    }
    //private
    Yi.Workflow.prototype.findCurrentTaskById 
    = function(taskId){
             
    for(var i=0;i<this.currentTasks.length;i++){
                       
    if(this.currentTasks[i].id == taskId)
                                
    return this.currentTasks[i];
             }
             
    return null;
    }
    //private
    Yi.Workflow.prototype.removeFromCurrentTasks 
    = function(task){
             
    var temp = [];
             
    for(var i=0;i<this.currentTasks.length;i++){
                       
    if(!(this.currentTasks[i] == task))
                                temp.push(
    this.currentTasks[i]); 
             }
             
    this.currentTasks = temp;
             temp 
    = null;
    }
    //public觸發當前環節
    Yi.Workflow.prototype.signal 
    = function(taskId){
             
    //只處理當前活動環節
             
    var task = this.findCurrentTaskById(taskId);
             
    if(task == null){
                       alert(
    "工作流未流轉到此環節!");
                       
    return;
             }
             
    //對于合并的處理
             
    if(task.merge != undefined){
                       
    if(task.merge != 0){
                                alert(
    "工作流流轉條件不充分!");
                                
    return;
                       }
    else{
                                Yi.Utils.execute(task.before);
                       }        
             }
             
    //觸發當前環節
             Yi.Utils.execute(task.fn);
             
    //觸發后動作
             Yi.Utils.execute(task.after);
             
    //下一步如果工作流結束
             
    if(task.next === "EOWF"){
                       Yi.Utils.execute(
    this.def.end);
                       
    delete this.currentTasks;
                       
    return;
             }
             
    //遍歷下一步環節
             
    this.removeFromCurrentTasks(task);
             
    for(var i=0;i<task.next.length;i++){
                       
    var tempTask = this.findTask(task.next[i]);
                       
    if(!tempTask.inCurrentTasks)
                                
    this.currentTasks.push(tempTask);
                       
    if(tempTask.merge != undefined){
                                tempTask.merge
    --;
                                tempTask.inCurrentTasks 
    = true;
                       }
                       
    else
                                Yi.Utils.execute(tempTask.before);
             }
    }
    //public獲取當前的活動環節
    Yi.Workflow.prototype.getCurrentTasks 
    = function(){
             
    return this.currentTasks;
    }
    //public獲取流程定義
    Yi.Workflow.prototype.getDef 
    = function(){
             
    return this.def;
    }

     

    ////////應用系統///////////////
    var wf = new Yi.Workflow(workflowDef);
    alert(
    "啟動工作流");
    wf.start();
    alert(
    "嘗試手工執行任務3,返回工作流沒有流轉到這里");
    wf.signal(
    "task3");
    alert(
    "分支開始");
    alert(
    "手工執行任務1");
    wf.signal(
    "task1");
    alert(
    "手工執行任務2");
    wf.signal(
    "task2");
    alert(
    "手工執行任務4");
    wf.signal(
    "task4");
    alert(
    "手工執行任務5");
    wf.signal(
    "task5");
    alert(
    "手工執行任務3");
    wf.signal(
    "task3");
    function begin(){
             alert(
    "流程開始,該函數在外部定義");
    }
    function end(){
             alert(
    "流程結束");
    }

    posted @ 2009-03-06 17:39 楊一 閱讀(1991) | 評論 (1)編輯 收藏

    Spring Security 2 中動態角色權限的實現

     

    安全框架的主體包括兩部分即驗權和授權。Spring Security2可以很好的實現這兩個過程。Spring Security2對其前身acegi最大的改進是提供了自定義的配置標簽,通過Security的命名空間定義了httpauthentication-provider等標簽,這樣做的好處是極大地簡化了框架的配置,并很好地隱藏了框架實現的細節,在配置的表述上也更清晰,總體上提高了框架的易用性。

    然而,該框架默認的權限配置方式在xml中,又因為新版本隱藏了實現細節,在動態權限的擴展上,能力變小了。在驗權過程中,遇到的問題不多。但在授權時,如果是acegi,人們可以通過繼承AbstractFilterInvocationDefinitionSource類實現在授權(即資源角色和用戶角色的匹配)前,針對資源的角色的獲取。而新版本因為用新標簽進行了整合,這個過程被默認的類實現隱藏掉了,包括過濾器,資源獲取和角色定義等過程都由框架來實現,于是很多人在使用Spring Security2時也想通過改動DefaultFilterInvocationDefinitionSource對資源的獲取來實現數據庫或文件中的動態的角色。不過這樣的改動侵入性比較高,而且還保留了acegi的痕跡,也違背了開閉的原則。

    其實,我們完全可以通過Spring Security2 accessManager提供的自定義投票機制來解決這個問題,這樣既不影響現有的基于URL的配置,還可以加入自己的動態的權限配置。

             其實現策略如下:

    1 定義類DynamicRoleVoter實現AccessDecisionVoter,注入實現接口DynamicRoleProvider(用來定義獲取角色的方法)的提供動態角色的類

    2 在兩個supports方法中返回true

    3 vote方法中,有三個參數(Authentication authentication, Object object,

    ConfigAttributeDefinition config) 通過第一個獲取用戶的權限集合,第二個可以獲取到資源對象,進而通過DynamicRoleProvider獲取到角色集合進行匹配。

    4 在配置文件中加入DynamicRoleVoter,如下:

    <beans:bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
    <beans:property name="decisionVoters">
    <beans:list>
    <beans:bean class="org.springframework.security.vote.RoleVoter" />
    <beans:bean class="org.springframework.security.vote.AuthenticatedVoter" />
    <beans:bean class="DynamicRoleVoter">
        
    <beans:property name="dynamicRoleProvider">
            
    <beans:ref local="dynamicRoleProvider"/>
    </beans:property>
    </beans:bean>
    </beans:list>
    </beans:property>
    </beans:bean>
    <beans:bean id=” dynamicRoleProvider” class=”…”>
        
    ……
    </beans:bean
    >

    posted @ 2009-03-04 12:55 楊一 閱讀(5074) | 評論 (0)編輯 收藏

    實現Ext表單對checkBoxGroup的統一管理

    1 對于類型是checkboxgroup的數據,數據庫中保存數據的格式是value1,value2...valueN,其中1~N的數據有可能不存在,如果選中則存在,最后拼接成一個串。
    在Ext中,通過Record對象向FormPanel中的內置對象BasicForm加載數據時,采用的是setValues方法,而setValues第一步要通過Record中定義的name使用findField方法找到表單元素,遺憾的是,繼承了Field的checkboxgroup組件并不能正確的通過getName返回自身引用,所以,需要對getName方法進行重寫,此外,為了適應我們采用的數據格式,對于該組件的setValue(被setValues調用)和getValue(獲取到已加工的數據,此事后話)也要進行重寫。故而對于形如:
    {     
       xtype: 'checkboxgroup',   
       name: 'biztype',     
       width: 
    220,   
       columns: 
    3,   
       fieldLabel: '業務類別',   
       items: [     
           {boxLabel: '類別1', inputValue: '
    01'},     
           {boxLabel: '類別2', inputValue: '
    02'},     
           {boxLabel: '類別3', inputValue: '
    03'},     
           {boxLabel: '類別4', inputValue: '
    04'}     
           ]     
     }  

    的checkboxgroup定義,需重寫類如下:
     
    Ext.override(Ext.form.CheckboxGroup,{    
        
    //在inputValue中找到定義的內容后,設置到items里的各個checkbox中    
        setValue : function(value){   
            
    this.items.each(function(f){   
                
    if(value.indexOf(f.inputValue) != -1){   
                    f.setValue(
    true);   
                }
    else{   
                    f.setValue(
    false);   
                }   
            });   
        },   
        
    //以value1,value2的形式拼接group內的值   
        getValue : function(){   
            
    var re = "";   
            
    this.items.each(function(f){   
                
    if(f.getValue() == true){   
                    re 
    += f.inputValue + ",";   
                }   
            });   
            
    return re.substr(0,re.length - 1);   
        },   
        
    //在Field類中定義的getName方法不符合CheckBoxGroup中默認的定義,因此需要重寫該方法使其可以被BasicForm找到   
        getName : function(){   
            
    return this.name;   
        }   
    });

    2 通過內置對象basicForm的getValues方法可以獲取到一個form的完整json數據,但遺憾的事,這里取到的是dom的raw數據,類似emptyText的數據也會被返回,而Field的getValue方法不存在這個問題,所以如果想要返回一個非raw的json集合,可以給formpanel添加如下方法:
    getJsonValue:function(){   
        
    var param = '{';   
        
    this.getForm().items.each(function(f){   
            
    var tmp = '"' + f.getName() + '":"' + f.getValue() + '",';   
            param 
    +=  tmp;   
        });   
        param 
    = param.substr(0,param.length - 1+ '}';   
        
    return param;   
    }  

    這個方法同樣適用于上面定義的checkboxgroup,如此就可以把前后臺的數據通過json統一起來了

    posted @ 2009-03-04 12:50 楊一 閱讀(2235) | 評論 (0)編輯 收藏

    富客戶端技術中的JavaScript腳本國際化

         摘要: 當前的富客戶端可以包含兩部分:分別為JSP頁面和通過富客戶端js組件(如extjs)渲染的組件化窗口頁。針對這兩部分分別做如下處理: 對于JSP頁面的部分采用JSTL標準庫的fmt標簽,如通過: <fmt:message key="page.login.title"/>這樣的形式進行展現,其中message對應的文本在服務端配置,并在web.xml中配置資源文件的位置,也可以采用s...  閱讀全文

    posted @ 2008-12-23 12:07 楊一 閱讀(2564) | 評論 (1)編輯 收藏

    windows中不能雙擊打開jar文件的解決辦法

    看此文前請保證jar包中有至少一個Main方法入口,及圖形化的界面。
    并保證META-INF/MANIFEST文件中的Main-Class已經指向之前實現的main方法入口。

    最近硬盤壞了,于是重新安裝了OS,發現拷貝后的jdk或jre(未經安裝的版本),不能打開jar文件執行(jdk版本1.6_11),
    于是在打開方式中指向了javaw程序,發現無效,并提示"cannot find main class", 與此同時windows把jar類型的文件關聯到了
    指定的javaw程序上,上網找了一通,沒有人提及這個問題的解決辦法,而顯然這個問題又不是由開篇中提到的問題導致的。
    于是在注冊表中當前用戶配置中刪除了當前jar類型的定義。但是重新嘗試后依然無效。

    于是重新安裝了jdk,發現這次可以打開jar文件了,并且這次用來打開的程序從打開方式來看仍然是javaw。
    比較注冊表中文件類型的定義,并沒有差別。從文件夾選項 -> 文件類型來看終于看到了差別,

    高級里面的open操作定義如下:
    "C:\Program Files\Java\jre6\bin\javaw.exe" -jar "%1" %*
    而如果我們自己選擇javaw,默認的open操作是沒有 -jar參數的,必須手工加進去。
    我們知道java啟動jar包的參數是 -jar,但是記得以前javaw是可以直接打開jar的,不知什么時候起也需要帶有-jar參數了。

    所以對于一個拷貝的綠色jre只要修改一下open操作的定義就可以解決上面的問題了。

    解決了上面的問題,又產生了新的問題,之前選擇打開的javaw程序在打開方式中丟不掉了,比較多余,這個可以在注冊表中修改
    在HKEY_CLASSES_ROOT\Applications下面找到響應的程序刪除就可以了,原來每次用一個程序打開一個類型的文件windows都會在
    注冊表中這個地方留下相關的記錄

    posted @ 2008-12-22 18:19 楊一 閱讀(3182) | 評論 (0)編輯 收藏

    關于文本協議中二進制到文本的轉碼

    偶然間注意到一個困擾了我很久的問題,那就是如果我不通過Socket而通過應用層的某種基于文本的協議,比如SOAP進行通信的話,
    如何傳遞二進制的數據呢?現在SOA,Web Service等很火,應該會遇到這種問題吧?

    現在已知的方法可以通過Base64進行編碼,其原理和方法見:
    http://baike.baidu.com/view/469071.htm

    這種方法采用了字節中的6位進行文本轉換,并且在其他論壇上也看到了帖子說淘寶的搜索也采用了這種編碼方式進行處理。
    但是采用了5位進行轉換。并且大膽地給出了5位轉碼的算法,見:
    http://www.javaeye.com/topic/286240

    不過這種5位的轉換會產生更多多余的字節,6位的轉碼充分利用了現今的可讀文本,可是5位卻沒有,因為5和8的最小公倍數是40,
    所以當每轉換40位即5個字節的二進制數據需要8個字節來表示,這樣就多產生3個字節,浪費的效率是3/5, 而6位轉碼浪費的效率是
    1/3。而且隨著字節增多,轉化效率也在下降??梢姴捎?位轉碼是一種既浪費空間,又浪費效率的解決方案。在不增加url長度的情況下充分提高效率,6位編碼是最佳的。如果可以任意的餓犧牲url長度,
    可以把0-9全部拿出來當做標記位,0-9不會單獨出現,這樣一共有10*26 + 26 = 286 種可能還不包括小寫字母,
    此外還有=,+,-什么的至少256可以編碼8位的字節了,這樣處理效率就提高了。

    現在把問題優化一下,人類可讀無歧義的文本碼有0-9,A-Z,a-z共62個
    設取出x個作為標志位則(62-x) * x + (62 - x) >= 256
    解這個二元一次方程得到:
    3.366<=X<=57.634
    考慮到編碼的文本長度,取x的最小值,即 4
    最優解:
    用0, 1, 2, 3做為標志位
    4-9,A-Z, a-z參與編碼并與標志位配合實現8位字節的文本化
    可以看到這種方法的轉碼效率會比較高,但是空間冗余大。

    此外其實可用的文本不知62個,包括感嘆號等用上后補足64 = 2^6
    它的高位是 00
    那么只要再找到三個文本符保存其他三個高位01 10 11就可以了
    這樣的轉碼空間可以更小一些。


    想法還很不成熟,歡迎大家批評

    posted @ 2008-12-04 15:56 楊一 閱讀(1661) | 評論 (2)編輯 收藏

    JSON通用服務端處理

         摘要: 最近在學習JavaScript,發現不論是ext還是prototype都很推崇json這種通信協議的格式,但是這兩個框架都是比較偏前端的,和dwr不同,dwr是一個一站式的ajax框架,不僅提供了客戶端的工具方法,也包括服務端的配置和通信的處理。 而ext和prototype等僅僅設置好了json的接口并對ajax通信做了封裝,相對而言是一種比較“純粹”的AJAX實現,當...  閱讀全文

    posted @ 2008-11-24 18:14 楊一 閱讀(2229) | 評論 (1)編輯 收藏

    軟件開發平臺及框架的意義

    學過軟件工程的都知道,軟件產品的生產周期是一個經歷若干階段的漫長過程,包括需求獲取 - 設計 - 開發 - 維護等等。

    需求階段 - 總想考慮到所有的問題,或是一切按合同辦事。但在現實中根本不得能,因此很多公司開始提倡“隨需而變”的能力,希望快速的響應用戶的需求變化
    維護階段 - 總希望自己開發出來的東西一勞永逸,永遠不要再產生任何麻煩,產生了麻煩也不要找到我。甚至有些項目組的人員開發出來一大堆不成熟的產品、項目后撒手不管,走人了事,毫無職業操守,亦是對自身行業聲譽(至少是國內IT服務提供商聲譽)的一個打擊。真正的項目開發不應該這樣,一定是非常易于維護的,能夠快速地找出問題的所在,或是新需求切入點的所在,然后解決

     

    很明顯,前面提到的兩個問題都要也只能通過設計和開發來解決。問題來了,怎樣開發出的軟件才能快速地響應需求,易于維護?可以有很多不相沖突的說法,比如解耦,比如通過POJO封裝數據等等。這些東西流行開來以后,很多人有疑問,為什么我的項目中一定要用這些框架?我不用這些框架也可以快速的開發出我要的功能,而且更加簡單,等等。如果孤立地從設計和開發的角度看這些問題,這種說法并沒有錯誤,但是如果從整個軟件開發的生命周期來看,則不是這樣。當然,這里還有一個是否“過度設計”的trade-off在里面,不過那又是另一個話題了。

    再說說各種各樣的平臺吧,它們和框架不同,軟件體系結構中有一種架構模型即層次模型,我們現在的TCP/IP協議棧即屬于這種模型,我們的軟件對于平臺產品的依賴是一種朝向穩定的依賴,就好像我們在調試代碼時往往不會去調試操作系統API的bug一樣,因此在開發這種平臺層次級別的產品時就沒有必要再去采用那些為了保障“企業應用”Web軟件生命周期中所采用的方法了,完全可以用最基礎,最底層的手段。只要能夠做到高效、穩定即可。因此,平臺中間件產品的開發必須和應用軟件產品分開來看,雖然它們可能都在用Java這種編程語言。

    posted @ 2008-10-26 13:24 楊一 閱讀(1635) | 評論 (0)編輯 收藏

    形式化與自動化

    本科讀書時,曾聽過離散數學老師一句很精彩的論斷:“只要能夠形式化的東西,就可以自動化”??墒墙裉煳也徽勲x散數學,倒想說說其他不相關的東西。

    你一定聽到過“一流的企業賣標準,二流的企業賣品牌,三流的企業賣產品”。

    什么是形式化?為什么形式化的東西就可以自動化呢?撇開數學符號不談,對企業來說,形式化的東西可以是一些規章及做事的方法,生產產品的方法等等。為什么人民幣稍一升值,中國的中小制造型企業就要痛苦不堪?因為我們每既沒有品牌,更沒有標準,拿生產DVD機為例,最初的90年代生產DVD機賣到歐美很賺,可是現在據說不賠錢就不錯了,因為要繳納一大筆的專利費。中國的大多數企業處在整個產業鏈的最下端,所得的利潤的大多數被其他企業剝奪了,因為我們用的是別人的品牌,別人的標準。我們的公司在全球經濟中處于“民工”的境地。

    回到我們的問題中來,一流企業所做的標準,是生產一種產品的方式和規約,這一層面是抽象世界的最高層次,無法對這層抽象的產生進行形式化,所以是一種創造性的勞動;二流企業所賣的品牌是對一種產品具體生產方法的規約,通過“模板方法模式”的應用,這一層次的抽象的模板可以從上一個層次的形式化中自動化,然后只需形式化具體的操作細節,對于生產細節的形式化,也需要一定的創造性的勞動;三流的企業是一部機器,因為到此為止一切的東西都已經形式化了,只需要有時間和精力的機器去自動完成而已。

    讓我們好好想想在一個知識經濟的社會里,什么事物是創造性的,是只能被形式化而不能被自動化的。因為只有人類的創造性思想不能被機器所取代,這也是為什么機器人無法取代人類的原因。

    80年代出生的人應該記得歷史教科書中的論述,工業革命時,一些工人去砸毀機器,覺得這些機器剝奪了他們的工作。如果有一天,老板突然來告訴您:你可以離開了,請不要沮喪和懊悔,因為您早該意識到您其實就是一部機器。當然為了防止上面悲劇的發生,早點去從事有創造性的工作吧,停止對于各種軟件自動化輔助工具的抱怨和擔憂,勇敢地迎接明天。

    還記得小時候常看的電影《神鞭》吧!

    posted @ 2008-09-12 12:49 楊一 閱讀(1490) | 評論 (1)編輯 收藏

    也談普元

    偶然間看到下面有一個網友慨嘆普元的強大,而開發人員的渺小。
    小弟剛剛參加工作,也在項目中接觸到了普元的EOS。普元的這個東西怎么說呢,就是亂用XML然后Spring沒做好就變成那個樣子的,同時失去了類型的表述,一部機器要進行裝配需要組件和零件,軟件應該自上而下,分而治之,這是上個世紀70年代,學者們就達成的共識,所以關于“銀彈”神話的唯一結論就是——“沒有銀彈”。
    為什么說EOS是沒有做好的Spring?
    Spring簡化了對象的裝配,強調重用,是建立在面向對象基礎上的,是建立在敏捷測試基礎上的,是建立在強類型基礎上的;
    而EOS則是建立在面向過程的基礎上的,建立在不可測試的基礎上的,建立在毫無類型基礎上的(全是String)
    然而EOS也有很多的優點(據小弟不完全發現):
    1)EOS固化的開發流程強制一個team從一種易于維護的結構組織Web,包括頁面,表示層,邏輯層等等。否則的話就需要一個架構師來做出規約,但仍不易于管理;
    2)EOS的畫圖功能讓人耳目一新,從“代碼即文檔”的哲學出發,這些畫圖很好地詮釋了代碼表述的內容和結構,給程序的維護帶來便利。
    3)相對于OO和J2EE傳統開發,EOS易于上手,學習曲線較短。但是這一點有爭議,EOS的知識不具備通用性。
    綜上,根據2-8的關系法則,在某些領域EOS的確有其優點,但是認為EOS完全“解放”了程序員,則是不負責任的說法。
    這只是我的個人看法,歡迎大家就此話題討論。

    posted @ 2008-09-04 15:41 楊一 閱讀(2334) | 評論 (10)編輯 收藏

    標準化與非標準化

    聯合國是國家間的標準化組織,可惜一超多強們只把它當作是一個道具,需要時拿來用一下,不需要時完全可以踢到一邊。
    歷史上,每個朝代都認識到失敗的教訓,卻最終都迎來了相似的結局。

    有人說,軟件行業是一個天生的壟斷行業,其特質是,產品研發的過程異常的復雜,但產品一旦出爐,就可以瘋狂而不計成本地生產。只有NRE Cost,眼下的軟件行業也出現了群雄逐鹿的場面,上游產業被幾家大公司所瓜分,這些大公司如果能互相牽制,則會坐在一起,成立一個組織。如果一家獨大,則我行我素,稱王稱霸。

     

    看看他們的一路成長:

    初始:

    他們想占有你,最初都會很乖。就像女孩在成為家庭主婦之前總能收到男孩的禮物。

    成長:

    大公司們也漸漸從規范的參與者,變成規范+的樹立者,比如IE,Office,再比如Oracle的特性等等。

    稱霸:

    市場占有的差不多了,可以不顧客戶的意志做自己喜歡的事情,反正你已經離不開我。

    消亡:

    客戶們怨聲載道,新的“符合規范”或簡潔明了的產品出現市場。


    希望這些大軟件廠商們能夠吸取教訓,也希望小軟件廠商們將來不要走入歷史的怪圈。

    posted @ 2008-09-03 18:54 楊一 閱讀(188) | 評論 (0)編輯 收藏

    OA的殺手級應用

    在遠古時期人們靠結繩紀事,據說美洲的瑪雅文明在覆滅之前都一直沒有自己的文字,而采用這種古老的方法。
    后來我們的祖先發明了文字,在竹簡上,布帛上書寫文字,竹簡和布帛就是信息的載體,這樣的載體造價不菲,所以我們的文言和白話就有這么大的差距,留下的論語也要微言大義。再后來我們的祖先發明了紙張,嚴重地降低了承載信息的開銷,于是人類的文明得以更好地記錄和更快地發展。今天,我們的信息載體又有了新的變化,一張光盤,一個硬盤都可以承載無數的學問。
    信息有了載體,隨之產生了信息管理的問題:
    如何對信息進行增刪改查的操作?如何處理附加在信息上的工作流?如何管理權限?
    在IT系統出現之前,人們通過圖書館管理圖書,通過搬運工進行信息流動,通過鑰匙和鎖頭管理權限。
    在IT系統出現之后,人類可以通過計算機來完成這些操作。這其中數據庫系統,工作流系統幫了我們大忙。
    IT系統處理信息的過程是:
    多樣式的文檔(抽象為XML)-> 內存數據 -> 持久化(文件數據庫)->內存數據 -> 多樣式的文檔(抽象為XML)
    我們讓用戶在我們設定的UI窗口進行輸入,然后通過報表的形式產生各種各樣的輸出。中間一個步驟一個步驟,一個環節一個環節的走下去。
    這其中,我們把很大一部分精力消耗在,如何讓用戶舒服的輸入,和產生用戶所需要的輸出上。
    于是人們就要思考,難道不可以直接編輯一種全世界認可的文檔形式(大家現在正在和MS爭吵的東西)通過網絡流轉,然后獲得結果嗎?為什么要從程序員的角度加給用戶數據庫,工作流,錄入界面這些繁雜的東西?
    從這點意義上說,我推崇Sharepoint或者Google sites。
    就拿google docs來說吧,在線編輯word,每個環節通過分享的形式進行編輯,授權這部分可以通過歷史記錄來找到,隨時恢復到歷史版本,因此也不怕誤操作和無權限操作,真正的所見即所得,支持50人同時在線編輯。這難道不是OA的殺手級應用嗎?

    posted @ 2008-03-05 13:31 楊一 閱讀(1259) | 評論 (1)編輯 收藏

    如何學習spring

    學習這些框架技術,我覺得歸根結底就是做什么的,為什么做,如何做
    前人說讀書有三個層次,我看這大概可以總結為是新的三個層次:)
    因為沒有搞清楚為什么要用,就會誤用,用了還不如沒用。其實我覺得學spring讀讀rod那個原著挺好的,比單純學spring有幫助,最好自己有體會。比如你開發網站很熟練了,自然就知道為什么要用spring了。等完全領會了他那兩本書后,再讀讀他們的reference book應該差不多了。
    這個過程其實就是做什么->為什么->怎么做的過程

    posted @ 2008-01-16 10:19 楊一 閱讀(1026) | 評論 (1)編輯 收藏

    系統模型及系統故障日志的思考

    最近在研究關于系統的基于日志的故障恢復,無意間在網上發現一篇論文中對于系統日志模型的精彩論述,翻譯過來并附上我的思路:

    一個系統是一個具有明顯的邊界的實體,它根據一定的輸入,自身運行邏輯及系統的內部時鐘變化來產生相應的輸出。
    所謂“明顯的邊界”是指系統所產生的輸出是明確而無二義性的。我們稱這個邊界為系統的設計規范(specification)。一個系統通過與其所處環境進行交互,從而獲取輸入并產生輸出。一個系統可以被拆解為不同的子系統。這些子系統通常被稱為系統模塊(system components),每個模塊又獨立地成為一個系統,作為一個系統,這個模塊又會和它的相關環境進行交互(比如,一個更大的系統中的其他的模塊組件)來獲取輸入并產生輸出,這些模塊還可以繼續被分解為更小的子系統。
    一個系統可以被建模為一個狀態機(state machine),其中的狀態包含了系統所持有并處理的數據。這些狀態的遷移被分為兩大類:由系統內部邏輯所觸發且對外部環境透明的遷移和直接與外部環境相接觸的遷移。前者的例子如內存數據和寄存器數據的轉換,內存中數據結構的重組。第二種遷移的例子包含了各種各樣的系統和環境之間的交互,一般來說,如果這個過程能被建模成系統的I/O操作,則應屬于這一類別。因此,一個消息內容的形成是一個或多個第一類別狀態遷移的結果,但將消息輸出到系統的環境則是屬于第二類遷移。
    第二類別的狀態遷移可以捕獲交互事件(interaction events),或者簡單的事件(events)。這些事件可以由系統外部的觀察者(observer)來獲取。顯然,這里的事件是消息、信號、數據及其內容以及一切系統和其環境交互(如機器人運動手腳,報警器報警,打印機打印等等)的發送和接受的模型。此外事件還可以用來描述系統缺乏交互的時間,比如一個計時器在日志中輸出系統的空閑時間等。
    當一個大的系統被拆分成多個模塊時,每個模塊都被賦予了整個系統所處理數據的一部分,正因為模塊和模塊間的接口銜接和數據感知,一些原來屬于第一類別的狀態轉換,因為系統的拆分在更低的層次上變成了第二類別,成為系統和環境之間的交互。
    對于一個特定的系統,他對于輸入的形式和獲取時間是不可預知的,但是這個系統卻應該能夠做到根據一個特定的輸入以及系統當前的特定狀態獲取一個特定的輸出。因此系統的執行可以被建模為狀態轉換序列,每個狀態的輸入是一個不確定性事件。為了記錄日志并做到故障恢復,我們還應做到能夠在環境中捕獲這個不確定性事件輸入。
    此外,在系統與系統間進行交互式,事件的傳遞時間也應該是不確定性的。



    怎樣用日志來預防系統崩潰,在崩潰后如何還原系統,我想關鍵問題就是怎么做好內存的快照,這樣,在斷電重啟后可以通過日志來還原內存的信息這樣第一步就是確認內存中的數據結構,哪些是必不可少的。確定系統恢復的粒度,按照子系統的分割和事件的記錄來進行replay,根據子系統的劃分,可以找出每個子系統中第二類別的事件進行記錄。
    以向數據庫系統提交作業為例,實際上在整個作業提交的過程中,每個層次都要做到可以在失敗的情況下重現,這個功能在完善的數據庫系統和集群批處理系統中當然已經很完善。但如果是針對web系統的作業提交,則需要針對Web的作業持久方案,做一個日志恢復處理。需要特別指出的是,對于數據的查詢是不需要做備份的。
    在具體實現上,我想應該包括:日志記錄,故障檢測,日志持久三個部分。一份日志就是一個對于系統檢查點(checkpoint)的連續記錄。日志記錄者負責記錄日志到日志持久者,故障檢測器隨時監控系統,發現故障后,從日志持久者中讀取日志,進行replay.

    posted @ 2008-01-07 14:44 楊一 閱讀(994) | 評論 (0)編輯 收藏

    如何實現包含插件功能的Applet Web界面

    不知諸位有沒有想過用Applet來組織Web的程序界面?小弟最近整理了一些雜碎的思路,思想完全開放,歡迎批評。
    先說一下可能遇到的問題:
    1 安全性:Applet對本地資源的操作需要相應的安全許可;
    2 庫資源的下載:如何下載及管理支持本地Applet的庫資源;
    3 通信:Applet如何與后臺的Servlet進行通信;
    4 圖形的加載:如何利用Applet動態的實例化并展現界面。

    下面一一展開討論

    (一)保障安全性

    安全性的主要解決方案是利用Java提供的keytool生成一個keystore,并利用這個keystore對jar包進行signjar的操作。
    整個對Java文件的編譯,打包和signjar過程可以利用Ant來完成,比如下面的Ant腳本片段就是用來處理signjar的,大家也可以通過相應的Java命令直接處理:

    < target  name ="signjar"  depends ="jar" >
     
    < signjar  jar ="example.jar"
      keystore
    ="${basedir}/yangyi.keystore"  storepass ="mypassword"  alias ="mykey" ></ signjar >
    </ target >

    如果直接用命令,則其形式為:
    jarsigner [ options ] jar-file alias
    具體的操作方法可以參考下面的鏈接:
    http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/jarsigner.html
    通過這個signjar的操作,我們就給這個包中的類進行了一次數字簽名,這樣,當Applet進行默認許可之外的操作時,窗口將彈出這個數字簽名要求本地用戶進行確認,只有在確認后,用戶才可以繼續進行操作。

    這樣我們可以在第一次用戶運行Applet程序時,在用戶的許可下動態地在用戶的$user.home/.java.policy下生成一個授權文件,以后就可以執行需要的操作了,為保證客戶端的安全性,僅賦予用戶執行特定文件夾權限的權利,如(僅作為例子,可以根據需要自己配置文件和Socket訪問權限):
    grant codeBase "file:/home/yiyang/test/×" {
     java.security.AllPermission;
    };

    (二)下載并管理庫支持

    這個過程可以通過Java的URL類提供的openConnection方法來獲取一個InputStream從而獲取到遠程的資源(包括支持庫和配置文件兩部分)
    1)對于配置文件,因為其內容都比較少,而且比較簡單,可以直接通過輸入流來獲取,這是沒有異議的;
    2)對于庫文件,在下載之前先到我們管理的庫的目錄下找到版本索引文件(我們設定一個版本索引文件來管理升級),這個版本索引文件如下所示:

    time={資源獲取的時間戳}
    lib1.jar=1.0
    lib2.jar=1.1

    其中,服務器端也保留有一份這樣的版本文件,當下載庫文件時,首先對客戶端和服務端的庫的總時間戳進行比較,如果客戶端大于或等于服務端,則不需下載,否則,如果客戶端對應項目為空或者其總的時間戳小于服務端,則進一步比較內部庫文件的版本,發現版本低的庫或在客戶端不存在的庫后,自動到服務器上進行下載,在下載成功后,更新客戶端的索引文件。

    (三)通信

    這個問題小弟曾在以往的blog中有過詳細的討論,可以到http://yangyi.blogjava.net中的相應隨筆中找到答案??偟膩碚f,在類型協議并不復雜,且客戶端,服務端均為Java開發的情況下,應用Hessian是一個好的解決方案,需要指出的是Hessian中的代碼對客戶端來說并不是全部必須的,大家可以根據客戶端的使用情況對這個庫進行瘦身。只保留作為客戶端必要的類即可。

    (四)動態的實例化及插件結構

    我們要實現用戶界面的集成,從根本上說要解決下面的幾個問題:
    1)菜單集成
    2)支持庫集成
    3)集成點
    4)輸出變量
    對于客戶端為Applet開發的插件,我們把上面的四項配置統一在XML文件中進行描述定義。
    這里需要注意的是菜單要提供名稱,支持庫要提供下載路徑或者本地路徑,集成點我們希望是一個JPanel。
    在定義好XML后,可以到網址:http://www.flame-ware.com/xml2xsd/去獲得一個對應的schema,利用這個schema和JAXB提供的xjc工具,我們就可以生成對應的XML操作類,來對配置進行處理。
    對于菜單的集成可以動態地在JMenu中添加MenuItem(表示插件的功能)
    根據配置的支持庫的位置,我們可以通過Java的URLClassLoader對庫進行動態的加載,然后根據相應的集成點,獲取實例,這個過程的示例代碼如下所示:

    File f  =   new  File( " a.jar " );  // a.jar是我們從配置文件中讀取的支持庫
    URL url  =   null ;
    Class lib 
    =   null ;
    try   {
     url 
    =  f.toURI().toURL();
     lib 
    =  Class.forName( " Lib " true new  URLClassLoader(
       
    new  URL[]  { url } ));  // Lib是我們從配置文件中讀取的集成點
     JPanel plugin_panel  =  (JPanel)lib.newInstance();
     
    return  plugin_panel;
    }
      catch  (Exception e)  {
     e.printStackTrace();
    }

    對于輸出變量,其主要作用是用戶各個插件之間的信息交互,我們可以通過一個總的HashMap來實現,為避免變量值出現沖突,在變量名前自動加上插件名的前綴。
    如plug_in1的變量var1,其系統名稱為:plug_in1__var1.

    解決了上面的四個障礙,我們就可以把精力投入到具體的功能實現上了。

    posted @ 2008-01-02 15:07 楊一 閱讀(1732) | 評論 (4)編輯 收藏

    好好學習,天天向上

    總算找到了工作. 我會好好努力的. 此時此刻,更加明白,以往的一切榮與辱都成為過去. 又要踏上新的征程.

    posted @ 2007-12-30 12:56 楊一 閱讀(295) | 評論 (0)編輯 收藏

    myeclipse

    剛看了myeclipse,eclipse是一個很可怕的東西,它試圖讓所有的開發人員一打開電腦就不能夠離開它,還要在里面完成所有的工作。人們不至于反感它的原因是它是開源的,不受商業控制的。如果我們對于myeclipse過度依賴,必然最終走向對微軟嚴重依賴的老路。我不反對利用軟件盈利。但是自由的精神不應被改變。
    微軟和我們是原始的獵人與獵物之間的關系,虎與倀的關系,最終極的占有。我們這才生是MS的人,死是MS的鬼。

    posted @ 2007-12-28 11:39 楊一 閱讀(317) | 評論 (0)編輯 收藏

    利用JAAS及JNI實現在Java環境下的Unix/Linux權限認證

         摘要: 這篇隨筆談一談如何在Java環境下利用Unix/Linux的用戶名和密碼對用戶的權限作出過濾。為方便大家學習交流,本文中給出了源代碼,借此拋磚引玉,歡迎大家對這個簡單的登錄模型做出改進或者設計出自己的技術方案。
    由標題我們不難看出,與本文相關的知識點主要有3個:
    1 JAAS這個解耦設計的多層驗證方法(1.4后已歸入Java核心庫中)
    2 應用JNI訪問底層代碼,及JNI中簡單的類型匹配
    3 在shadow模式下,Unix/Linux系統的用戶驗證  閱讀全文

    posted @ 2007-12-12 16:50 楊一 閱讀(1471) | 評論 (0)編輯 收藏

    延遲加載技術及其在iBATIS中的實現

    O/R映射框架的延遲加載技術實現大體上有這么4種(參看Martin Fowler的意見):
    (http://www.martinfowler.com/eaaCatalog/lazyLoad.html)

    There are four main varieties of lazy load. Lazy Initialization uses a special marker value (usually null) to indicate a field isn't loaded. Every access to the field checks the field for the marker value and if unloaded, loads it. Virtual Proxy is an object with the same interface as the real object. The first time one of its methods are called it loads the real the object and then delegates. Value Holder is an object with a getValue method. Clients call getValue to get the real object, the first call triggers the load. A ghost is the real object without any data. The first time you call a method the ghost loads the full data into its fields.

    通過閱讀源代碼,發現iBATIS中的延遲加載是用上述方式中的虛擬代理實現的.

    在動態代理的實現上, iBATIS有Java動態代理和CGLIB兩種實現方案,iBATIS把用CGLIB實現的方案稱為Enhanced的方案,可見CGLIB的效率會比java的動態代理效率要高.
    在iBATIS首先判斷是否定義了延遲加載,如果定義了,則利用Lazy的Loader來提取數據(返回一個Proxy).如沒有執行對這個的任何操作,或者只是不再使用(finalize),則不做處理,否者就加載真正的對象.

    可以通過閱讀類
    com.ibatis.sqlmap.engine.mapping.result.loader.LazyResultLoader
    的源碼獲取更多的細節.

    posted @ 2007-12-09 19:17 楊一 閱讀(2051) | 評論 (0)編輯 收藏

    淺談Java中的通信機制及與C/C++ API的集成(下)

    接著上次的話題,今天我們聊聊gSOAP這個框架,我們把用C寫的舊有系統用gSOAP改造一下,通過SOA的形式發布出去。
    上文提到,利用gSOAP可以做到以下3點:
    1 一個Stand-alone的服務器外殼
    2 一個根據API程序自動生成的Web Services服務
    3 一個WSDL描述符文件

    客戶根據 WSDL 描述文檔,會生成一個 SOAP 請求消息。Web Services 都是放在Web服務器后面,客戶生成的SOAP請求會被嵌入在一個HTTP POST請求中,發送到 Web 服務器來。Web 服務器再把這些請求轉發給 Web Services 請求處理器。請求處理器的作用在于,解析收到的 SOAP 請求,調用 Web Services,然后再生成相應的 SOAP 應答。Web 服務器得到 SOAP 應答后,會再通過 HTTP應答的方式把信息送回到客戶端。
    WSDL是Web服務中客戶端和服務端溝通的橋梁,描述了對象提供的方法。SOAP幫我們制定了一份被官方認可的對象的封裝方法。有了WSDL,客戶端只關心如何把參數用Soap封裝起來發出去,并獲取結果。服務端只關心如何對Soap進行拆包->服務->封包。gSOAP可以幫我們實現上述過程中的拆包和封包,而我們可以只關心服務的實現。

    言歸正傳,在這里我們以一個簡單的實現加、減、開放的Web Services的服務為例子,介紹gSOAP的使用:
    為了發布這個Web服務,首先我們需要把服務的接口定義好,這個服務可能是一個現有服務的Adapter,為此我們定義頭文件
    calc.h:
    typedef double xsd__double;
    int ns__add(xsd__double a, xsd__double b, xsd__double &result);
    int ns__sub(xsd__double a, xsd__double b, xsd__double &result);
    int ns__sqrt(xsd__double a, xsd__double &result); 
    注意到這里面我們把double定義成了xsd__double(兩個下劃線),這是為了告訴gSOAP,我們需要的soap格式和WSDL格式是基于Document/literal的而非rpc/encoded.為了不把事情搞復雜,在這里我只能說,Java1.6自帶的Web Services工具只支持Document/literal格式的WSDL,所以我們生成這種格式的WSDL。至于這兩種格式之間選擇和他們的long story,大家可以參考下面的文章:
    http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/
    編寫好頭文件后,我們就可以利用gSOAP提供的工具進行生成了:
    /usr/lib/gsoap-2.7/bin/soapcpp2 -S -2 calc.h
    生成的主要文件詳見附件。
    下面我們實現calc.h中定義的函數:
    // Contents of file "calc.cpp": 
    #include "soapH.h" 
    #include 
    "ns.nsmap" 
    #include 
    <math.h> 
    int main()
    {
       struct soap soap;
       
    int m, s; // master and slave sockets
       soap_init(&soap);
       m 
    = soap_bind(&soap, "localhost"9999100);
       
    if (m < 0)
          soap_print_fault(
    &soap, stderr);
       
    else
       {
          fprintf(stderr, 
    "Socket connection successful: master socket = %d\n", m);
          
    for (int i = 1; ; i++)
          {
             s 
    = soap_accept(&soap);
             
    if (s < 0)
             {
                soap_print_fault(
    &soap, stderr);
                
    break;
             }
             fprintf(stderr, 
    "%d: accepted connection from IP=%d.%d.%d.%d socket=%d", i,
                (soap.ip 
    >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);
             
    if (soap_serve(&soap) != SOAP_OK) // process RPC request
                soap_print_fault(&soap, stderr); // print error
             fprintf(stderr, "request served\n");
             soap_destroy(
    &soap); // clean up class instances
             soap_end(&soap); // clean up everything and close socket
          }
       }
       soap_done(
    &soap); // close master socket and detach environment
    }
    // Implementation of the "add" remote method: 
    int ns__add(struct soap *soap, double a, double b, double &result) 

       result 
    = a + b; 
       
    return SOAP_OK; 

    // Implementation of the "sub" remote method: 
    int ns__sub(struct soap *soap, double a, double b, double &result) 

       result 
    = a - b; 
       
    return SOAP_OK; 

    // Implementation of the "sqrt" remote method: 
    int ns__sqrt(struct soap *soap, double a, double &result) 

       
    if (a >= 0
       { 
          result 
    = sqrt(a); 
          
    return SOAP_OK; 
       } 
       
    else
       { 
          
    return soap_sender_fault(soap, "Square root of negative value""I can only compute the square root of a non-negative value");
       } 
    前文提到過,我們不希望為了發布基于Web Services的C語言的API而開發或應用一個大的Web服務器。我們代碼中的main函數實現了一個最簡單的Web Server(基于Socket).這個Server利用gSOAP生成的API來提供針對SOAP的處理。
    下面我們把這個嵌入式的web server編譯,編譯的時候注意stdsoap2.cpp這個文件是從gSOAP包中拷貝而來,不是自動生成的,大家下載gSOAP后直接就能找到這個文件及其頭文件。
    g++ -o calcServer calc.cpp soapC.cpp soapServer.cpp stdsoap2.cpp
    一個以Web Servers形式提供的C API誕生了。
    在server端執行./calcServer

    下面討論如何用Java1.6的自帶工具生成一個客戶端stub:
    把gSOAP生成的WSDL拷貝到我們的Java開發環境中來,按照Web Services Server中定義的端口和服務器,配置參數生成客戶端Web Services代碼:
    /usr/lib/jvm/jdk1.6.0_03/bin/wsimport -extension -httpproxy:localhost:9999 -verbose ns.wsdl

    生成后,把這個環境添加到eclipse的編譯環境中來,然后在eclipse中建一個新的類:
    class Test {
        
    public static void main(String args[]) {
            Service service 
    = new Service();
            
    double h = service.getService().sub(200001);
            System.out.println(h);
        }
    }
    運行后得到結果19999.0

    總結:當集成Java和C兩種平臺時,我們可以有多種解決方案,但首先我們應該想到gSOAP因為它能夠很出色地完成任務。
    文中相關代碼:
    http://www.tkk7.com/Files/yangyi/gsoap.zip
    廣告:本人明年畢業,正在找工作,個人簡歷:
    http://www.tkk7.com/Files/yangyi/My%20Resume.zip

    posted @ 2007-12-06 12:19 楊一 閱讀(1776) | 評論 (2)編輯 收藏

    淺談Java中的通信機制及與C/C++ API的集成(上)

    背景:
    對于舊有系統的改造和升級,最苦惱的莫過于跨平臺,跨語言。我的一個朋友最近從Java專向了專攻.NET——因為.NET的CLR既有類似Java虛擬機概念這種已經被證明很成功的底層托管能力。又對于Windows的就有桌面應用提供了良好的兼容。
    最近我的一個個人項目也面臨著這樣的需求。一個C語言開發的中間件,通過API暴露給二次開發及插件應用?,F在由于對其應用的需求變得日趨復雜,而且正在脫離Unix的管理環境,走向基于JWS這樣的BCS管理。有朋友推薦我用JNI,但這樣一是增加了耦合度,二是讓Java睡在JNI感覺不太安穩。在認知了上下兩層的系統平臺后,問題變得明朗起來:如何在HTTP協議下實現Java和C之間的交互?
    思路:
    本人對Java比較熟悉,先從Java的角度入手,Java間的通信方法:
    1 通過URL,Applet/JWS訪問被影射到URL的動態資源(Servlet)
    2 通過URL,Applet/JWS訪問共享的靜態資源(Server定期更新靜態資源)
    3 通過序列化和反序列化,實現簡單對象的傳輸(比如Resin的Hessian框架就提供了這種通信的方式)
    4 通過一些工具做代碼生成,利用Web Services實現客戶端和服務端的交互
    此外脫離HTTP,還可以做RMI,socket編程

    現在問題是通信的一端由Java變成了C/C++, 于是, 解決方案1需要把動態資源由CGI來定義,而方案3變得不再適用。于是方案有:
    1 通過URL,Applet/JWS訪問被影射到URL的動態資源(CGI)
    2 通過URL,Applet/JWS訪問共享的靜態資源(Server定期更新靜態資源)
    3 通過一些工具做代碼生成,利用Web Services實現客戶端和服務端的交互(×××這是我們討論的重點×××)

    解決方案:
    現在針對上文提出的3中通信方式中的1和3談一談實現的方法,2的實現方案比較靈活,需要發揮大家的想象力了:)
    針對CGI:
    首先CGI可以配置在各種主流的服務器中作為后端的腳本運行。大家可能對Servlet更熟悉一些。
    CGI可以用腳本寫,也可以用C來實現。CGI被觸發后,通過系統的環境變量來獲得輸入,在處理完畢后向標準輸出中輸出結果。
    由此可以想見,Web服務器在接受到來自HTTP協議的請求后,首先把請求的參數獲取到,然后設置到環境變量里。
    根據對訪問的URL的解析和服務器自身的配置,找到服務于請求的CGI程序的位置,然后執行這個程序。
    這個程序被執行后通過環境變量得到了服務器先前設置在環境變量中的參數。在經過一些復雜的邏輯操作后,向標準輸出輸出結果。
    這個輸出又被Web服務器所捕獲,轉而傳遞回請求的客戶端。
    更多關于CGI的知識和理解,大家可以通過google來尋找答案

    上述CGI的方式可以讓我們直接獲取到結果,但是方案比較原始和基礎。其缺點有:
    1 需要自己制定類型傳輸協議,做封裝和拆封,否則只支持字符串
    2 我們不會為了要用C的API就給它裝一個或者自己實現一個Web服務器的,這讓我們的底層程序顯得蠢笨而冗余。我們希望能有一個超薄的Server外殼,
    在對API封裝后,通過某個端口進行開放即可。

    針對Web Servcies:
    Based on上面的兩個不足,我們只能把希望寄托在Web Services身上了,
    筆者在這里推薦給大家的是在C/C++很著名的Web Services工具gSOAP。大家可以到http://gsoap2.sourceforge.net/上去下載這個工具。
    通過這個工具,我們可以做到:
    1 一個Stand-alone的服務器外殼
    2 一個根據API程序自動生成的Web Services服務
    3 一個WSDL描述符文件

    有關基于gSOAP的Web Services C服務端和Java客戶端的運行機理,及通過Java客戶端訪問gSOAP的Web Services的過程中需要注意的問題(筆者費了一天周折才搞清楚),將在下一篇中描述

    廣告時間:
    本人是哈工大的研究生,明年7月畢業。正在找工作,如果有工作機會,別忘了通知小弟哦(contactyang@163.com)

    posted @ 2007-12-04 17:33 楊一 閱讀(2039) | 評論 (0)編輯 收藏

    好久沒有更新了

    好久沒有更新了,昨天在VMware上安裝了一個虛擬的局域網,并在其上 配置了NFS和NIS,以及公司的集群產品LSF


    posted @ 2007-10-11 18:02 楊一 閱讀(254) | 評論 (1)編輯 收藏

    建了一個工具類,送給需要拼接插入字符串的朋友們:)


    import  java.sql.Date;

    /**
     * 這個類用來對數據庫做插入操作時采用
     * 
    @author  yangyi
     *
     
    */


    public   class  InsertTool  {
        
    private  String tableName;

        
    private  StringBuffer sbColumnName;

        
    private  StringBuffer sbColumnContent;

        
    public  InsertTool(String tableName)  {
            
    this .tableName  =  tableName;
            sbColumnName 
    =   new  StringBuffer();
            sbColumnContent 
    =   new  StringBuffer();
        }


        
    public   void  add(String columnName, Object columnContent)  {
            insertColumnName(columnName);
            
    if  (columnContent  instanceof  Date)  {
                sbColumnContent.append(
    " , "   +  columnContent);
            }
      else
                sbColumnContent.append(
    " ,' "   +  columnContent  +   " ' " );
        }


        
    public   void  add(String columnName,  int  columnContent)  {
            insertColumnName(columnName);
            sbColumnContent.append(
    " , " + columnContent);
        }


        
    private   void  insertColumnName(String columnName)  {
            sbColumnName.append(
    " , "   +  columnName);
        }


        
    public  String getInsertSql()  {
            StringBuffer sb 
    =   new  StringBuffer();
            sb.append(
    " INSERT INTO  " );
            sb.append(tableName);
            sb.append(
    " ( " );
            sb.append(sbColumnName.substring(
    1 ));
            sb.append(
    " ) VALUES( " );
            sb.append(sbColumnContent.substring(
    1 ));
            sb.append(
    " ) " );
            
    return  sb.toString();
        }

        
        
    public   static   void  main(String args[]) {
            InsertTool it 
    =   new  InsertTool( " aa " );
            it.add(
    " a " , " d " );
            it.add(
    " b " , 10 );
            it.add(
    " c " , 1 );
            System.out.println(it.getInsertSql());
        }

    }

    使用方法非常簡單,看看Main就知道啦

    posted @ 2007-06-14 12:41 楊一 閱讀(872) | 評論 (1)編輯 收藏

    Are you a coder or a programmer?

    Are you trying to build software that is composed by software components provided by large companies, and still name it a system?
    Are you doing the job anybody can do if given enough time to read the technical handbooks and rebuild the system or you have created something?
    If you are in the same situation, you are probably a coder instead of a programmer.

    posted @ 2007-06-13 19:47 楊一 閱讀(300) | 評論 (1)編輯 收藏

    My Attitude Toward Linux Shell

    I must be an extremely patient user of Linux, for I have been trying to install all versions of Linux distributions lately, and getting used to the shell commands.

    I also read some books on the subject. To be honest, it is true that the command lines are more efficient, and this can be verified by the following calculation:

    Suppose a mouse have 3 keys, and the keyboard ten times more. The three key mouse can generate actions 3^2 when clicked 2 times, while the keyborad 30^2. Come on, no matter we clicked the keyboard or the mouse, we just clicked 2 times.

    posted @ 2007-05-31 23:08 楊一 閱讀(300) | 評論 (0)編輯 收藏

    <2007年5月>
    293012345
    6789101112
    13141516171819
    20212223242526
    272829303112
    3456789

    導航

    公告

    本人在blogjava上發表的文章及隨筆除特別聲明外均為原創或翻譯,作品受知識產權法保護并被授權遵從 知識分享協議:署名-非商業性使用-相同方式共享 歡迎轉載,請在轉載時注明作者姓名(楊一)及出處(www.tkk7.com/yangyi)
    /////////////////////////////////////////
    我的訪問者

    常用鏈接

    留言簿(5)

    隨筆分類(55)

    隨筆檔案(55)

    相冊

    Java

    其他技術

    生活

    最新隨筆

    搜索

    •  

    積分與排名

    • 積分 - 86414
    • 排名 - 667

    最新評論

    閱讀排行榜

    評論排行榜

    自強不息


    用心 - 珍惜時間,勇于創造
    主站蜘蛛池模板: 成年人网站在线免费观看| 一区二区三区AV高清免费波多| 亚洲人成亚洲精品| 色噜噜亚洲精品中文字幕| 国产精品亚洲mnbav网站| 免费在线观看a级毛片| 男人的天堂亚洲一区二区三区 | 人人狠狠综合久久亚洲| 亚洲中文字幕一区精品自拍| 精品国产成人亚洲午夜福利| 亚洲中文字幕无码mv| 亚洲中文无码mv| 亚洲精品9999久久久久无码| 亚洲a∨国产av综合av下载| 久久久久亚洲AV无码去区首| 免费国产a理论片| 一级毛片在线免费播放| 中国一级毛片免费看视频| 久久久精品午夜免费不卡| 91视频免费网址| 99久久综合国产精品免费| 蜜桃精品免费久久久久影院| 俄罗斯极品美女毛片免费播放| 中文字幕第一页亚洲| 亚洲AV福利天堂一区二区三| 亚洲视频欧洲视频| 国产99在线|亚洲| 在线观看亚洲电影| 二区久久国产乱子伦免费精品| 久久午夜羞羞影院免费观看| 日韩精品成人无码专区免费 | 中文字幕的电影免费网站| 18禁美女裸体免费网站| 我要看WWW免费看插插视频| 免费不卡中文字幕在线| 久久亚洲国产精品一区二区| 亚洲最新中文字幕| 另类小说亚洲色图| 精品四虎免费观看国产高清午夜| 国产卡一卡二卡三免费入口| 免费一看一级毛片人|