蕭登益 98598010 2010/6/14 - ntut.edu.twwkchen/courses/ooad/files/ref... · 2018-03-02 · ii...

165
Homework #7 ObjectOriented Analysis and Design Iteration 2: Use case and Domain Model 蕭登益 98598010 2010/6/14

Upload: others

Post on 09-Jul-2020

7 views

Category:

Documents


0 download

TRANSCRIPT

  •  

     

    Homework #7                                                                        

    Object‐Oriented Analysis and Design                       

    Iteration 2: Use case and Domain Model  

     

    蕭登益  98598010 2010/6/14  

  •  

  •  

     

    Table of Contents 

    1    Requirement Document ..........................................................................................1 1.1  Problem Statement ....................................................................................1 1.2  System Context Diagram............................................................................2 1.3  Summary of System Features.....................................................................4 1.4  Use Case Diagram.......................................................................................5 1.5  Use Cases....................................................................................................5 1.6  Non‐functional Requirements and Constraints .......................................14 1.7  Glossary....................................................................................................16 1.8  Software Environments............................................................................18 

    2  Domain class model .............................................................................................19 2.1  Domain class diagram showing only concepts ........................................20 2.2  Add associations.......................................................................................23 2.3  Add attributes ..........................................................................................25 

    3  Class Diagram.......................................................................................................26 3.1  Logical Architecture with UML package diagram ....................................26 3.2  Use‐Case Realizations with GRASP Patterns ............................................28 

    3.2.1  SSD ...............................................................................................28 3.2.2  Contract........................................................................................30 3.2.3  Sequence Diagram .......................................................................33 3.2.4  Design Class Model ......................................................................41 

    4  Implementation Class Model ...............................................................................42 4.1  Implementation Class Diagram................................................................42 4.2  Implementation class model and design class model difference ............44 4.3  Calculate Line of Code..............................................................................48 

    5  Programming........................................................................................................49 5.1  Snapshots of system execution................................................................49 5.2  Source Code Listing ..................................................................................53 

    6  Unit Testing.........................................................................................................106 6.1  Snapshots of testing result.....................................................................106 6.2  Unit Testing Code Listing ........................................................................107 

    7  Midterm review response list ............................................................................154 8  Measurement.....................................................................................................155 

  •  

     

    ii 

    Change History 

    Version  Author  Description of Version  Date Completed

    0.10  蕭登益  Project Proposal  2010/03/02 

    0.20  蕭登益 The Previous Project 

    Information 2010/03/07 

    0.21  蕭登益 Rewrite Problem Statement 

    with TA’s feedback 2010/03/10 

    0.22  蕭登益 Problem Statement 

    System Context Diagram 2010/03/12 

    0.23  蕭登益 Use Case & 

    Use Case Diagram 2010/03/15 

    0.24  蕭登益 Glossary & Non‐functional 

    Requirements and Constraints2010/03/17 

    0.30  蕭登益  Domain Class Concepts  2010/03/25 

    0.31  蕭登益 Add Glossary & Fix Use Case & 

    Fix Context Diagram 2010/03/30 

    0.32  蕭登益 Domain Class Associations & 

    Attributes 2010/03/31 

    0.40  蕭登益  Rewrite Use Case  2010/04/15 

    0.41  蕭登益  Logical Architecture  2010/04/16 

    0.42  蕭登益  Sequence Diagram  2010/04/19 

    0.43  蕭登益 Rewrite Sequence Diagram 

    with Demo feedback 2010/04/20 

    0.50  蕭登益  Rewrite Design Class Model  2010/04/30 

    0.51  蕭登益  Write Implement  2010/05/12 

  •  

     

    iii 

    0.60  蕭登益  Modify Use Case  2010/05/24 

    0.61  蕭登益  Modify Domain Model  2010/05/26 

    0.70  蕭登益  Modify Use Case  2010/06/07 

    0.71  蕭登益  Modify Domain Model  2010/06/09 

    0.72  蕭登益  Source Code and Test  2010/06/14 

       

  •  

     

    iv 

  •  

      

    1    Requirement Document 

    1.1 Problem Statement 

    SyncFree是在 2005年北科大軟體系統實驗室所發佈的個人資料同步軟體,它可以讓使用者可輕易同步兩個不同位置的檔案資料,且支援多樣性的通訊協定、同

    步策略與資料過濾器,提供使用者一個方便、跨平台且有彈性的資料同步工具。 

     對資料同步軟體使用者來說,他們最害怕是同步檔案的遺失,然而SyncFree

    在發佈之後得到眾多使用者的feedback1當中,卻發現SyncFree潛在著一些非預期的例外,而導致系統可能會有檔案損毀的風險。此外,隨著資訊科技與網際網路

    的蓬勃發展,SyncFree當初所訂制的規格也跟不上現在所流行的通訊協定與檔案規模。 

     因此,我們提昇 SyncFree的一些非功能性需求限制,並且將 Exception 

    Handling Construct導入至 SyncFree架構中,引用陳建村所提出的 Exception Handling Pattern來處理同步時例外,以增強軟體的強健度。且又增加額外的保護機制防止使用者在同步時不留意而將檔案刪除。我們的目標是修正 SyncFree既有的 Bug、增強 SyncFree處理例外的強健度,讓使用者能安心地使用 SyncFree。 

     2

                                                           1  就曾經有 SyncFree忠實使用者因為硬碟壞軌,重灌還原時才發現許多檔案已遺失,且備份刪除檔案的功能失效,欲哭無淚〒△〒 2  這邊所指的都是 SyncFree 2.0版本,因為有重大瑕疵而已經暫時下架,1.0 版本經過嚴格測試,沒有什麼重大問題仍可放心使用。 

  •  

     

    1.2 System Context Diagram 

    I feel safe

    圖 1 System Context Diagram  

    Source Data:同步的來源檔案,也就是欲備份的檔案。 FTP Server:利用 FTP 協定在本地電腦與伺服器之間傳輸檔案。 HTTP Server:利用 FTP 協定在本地電腦與伺服器之間傳輸檔案。 Local File System:本地端的檔案系統。如:硬碟、隨身碟、磁碟等。 View:呈現 SyncFree的使用者介面(User Interface)。 Domain:提供較高階的檔案同步服務。如:定時同步、同步策略等。 Service:提供檔案的複製、檔案的刪除、目錄的建立、目錄的刪除與提供目

    錄資訊等服務。此外還負責通訊協定的檔案操作服務。 Exception Handling:處理 SyncFree同步檔案時可能發生的例外,若是預期性

    的例外則執行重試動作,而非預期性的例外則 logging。  

     :  使用者直接操作 SyncFree友善的 User Interface  :  使用者可以操作 SyncFree設置備份來源路徑,以供 Service 層傳輸檔案。  : SyncFree的 Service 層可以支援 FTP協定傳輸檔案。  : SyncFree的 Service 層可以支援 HTTP協定傳輸檔案。  : SyncFree的 Service 層可以直接在本地端傳輸檔案。  : SyncFree的 View層會參考到 Domain層。  : SyncFree的 Domain 層會參考到 Service層。 ,, : SyncFree的 View、Domain、Service層都建構於 Exception Handling

    結構。  

     

     

     

     

     

     

     

  •  

     

       如上圖所示,SyncFree的運作十分簡單,使用者在使用 SyncFree時只要設置來源端、目的端與通訊策略,SyncFree便會將來源端的檔案同步到目的端。而因為 SyncFree在同步時若發生例外,SyncFree都能將同步檔案回復到例外發生之前的狀態,並且再重新進行重試動作,因此使用者可以放心地使用 SyncFree,不用害怕 SyncFree會因同步失敗導致檔案損毀。  

    SyncFree內部是由 Domain、View與 Service 三個元件所構成,為了提昇可擴充與可修改性,三者的 Reference關係是直線單一向的,View元件只會Reference到 Domain元件,Domain元件也只會 Reference到 Service 元件,且為了要增強 SyncFree的強健度,三者都使用 Exception Handling Construct結構,下層在發生例外時,系統會決定自行處理或通報上層。 

  •  

     

     

    1.3 Summary of System Features 

    Feature ID  Feature Description 

    FEA‐01 提供同步檔案時高強健度地處理傳輸,系統不會因為發生例外而

    導致檔案損毀或資料錯誤。 

    FEA‐02 提供檔案同步設定的管理。包含新增、刪除、修改與瀏覽同步設

    定。 

    FEA‐03 提供預覽將要被同步檔案的清單功能。包含瀏覽同步檔案清單與

    保留檔案。 

    FEA‐04 提供管理被刪除檔案的備份功能。包含瀏覽、刪除與復原備份檔

    案。 

    FEA‐05 提供執行大規模的檔案同步。包含同步單一檔案大小 50G的檔案或 20萬個檔案。 

    FEA‐06  提供支援 SSL加密協定的 Protocol。 

  •  

     

    1.4 Use Case Diagram 

    SyncFree

    User

    Synchronize Partial File

    Manage Deleted Backup File

    By 登益

    圖 2 Use Case Diagram (Fully Dressed)  

    1.5 Use Cases 

    Fully dressed Use Case  

    Use Case ID  Use Case Name UC‐01  Synchronize Partial File. UC‐02  Manage Deleted Backup File. (本次實作及分析 Use Case) 

    Use Case ID  UC‐01 Use Case Name  Synchronize Partial File (同步部份檔案) Scope  SyncFree application Level  User goal Primary Actor  User Stakeholders & Interests 

    User:欲在真正同步前,得知同步後將有哪些檔案被刪除或覆蓋,並可選擇不想同步的檔案後才進行同步。 

    Preconditions  使用者已經新增一個以上的同步設定。 Postcondition  1. 不想同步的檔案資料沒有被同步。 

    2. 要同步的檔案資料已經同步。 

  •  

     

    Main Success Scenario 

    1. 使用者開啟 SyncFree。 2. 系統被開啟,並顯示 Synchronize Profile 列表。 3. 使用者選擇 Synchronize Profile的功能列表。 4. 系統顯示 Synchronize Profile 的資訊。 5. 使用者選擇瀏覽同步清單。 6. 系統依據 Profile 的資訊,產生同步列表。 7. 系統顯示同步列表與更動情形。 8. 使用者選擇不想同步的檔案。 9. 系統顯示該檔案不會同步。 10. 使用者選擇同步。 11. 系統關閉同步清單,並同步清單上的檔案。 12. 系統顯示已同步完成所有檔案。 

    Extensions  *a.  系統無法從 Synchronize Profile 取得同步設定資料:1.  系統取消動作。 

            2.  系統回報使用者無從同步設定取得資料。  *b.  系統無法取得本地端時間:         1.  系統回報使用者無法取得時間。         2.  系統要求使用者輸入現在時間。         3.  使用者回應系統:               3a.  使用者輸入時間。 

    1. 系統依據使用者輸入的時間作為現在時間。 

                  3b.  使用者選擇取消。 1. 系統取消動作。 

     6a.    系統讀取 Synchronize Profile 的同步策略: 

    1. 系統選擇同步策略方法。 1a.  同步方法為 SimpleCopy Strategy: 

    1. 系統將存在 Source且不存在於Destination 的檔案列為複製的對象。 

    2. 系統將存在於 Source與 Destination,但 Source的最後修改時間不等於Destination 的檔案列為複製的對象。 

    1b.  同步方法為 ExactCopy Strategy: 1. 系統將存在於 Source,不存在於

    Destination 的檔案列為複製的對象。 2. 系統將存在 Source與 Destination,但

  •  

     

    二者的最後修改時間不同的檔案列為

    複製的對象。 3. 系統將存在於 Destination,但不存在於

    Source的檔案列為刪除的對象。 1c.  同步方法為 Synchronize Strategy: 

    1. 系統將只存在於 Source或 Destination中任一方的檔案列為複製的對象。 

    2. 系統將同時存在於 Source與Destination 的檔案最後修改時間較大者列為複製對象,較小者列為刪除對

    象。 1d.  同步方法為移動(Movement): 

    1. 系統將 Destination 的檔案列為複製的對象。 

    2. 系統將 Source的檔案列為刪除的對象。 

     6b.  若來源目錄不存在: 

    1. 系統顯示錯誤訊息,並詢問使用者是否繼續進行同步。 

    2. 使用者回答系統。 2a.  使用者回答繼續: 

    1. 系統要求使用者重新設定來源目錄。 2. 使用者輸入目錄。 

    2a.  使用者輸入目錄存在: 1. 系統設置來源目錄。 

    2b.  使用者輸入目錄不存在: 1. 回到  6b Step 1。 

    2b.  使用者回答不繼續: 1. 系統停止設定來源目錄並關閉同步清

    單。  6c.  若目的端在本機上,且目的目錄不存在: 

    1. 系統顯示錯誤訊息,並詢問使用者是否繼續進行同步。 

    2. 使用者回答系統。 2a.  使用者回答繼續: 

    1. 系統建立目的目錄。 

  •  

     

    2b.  使用者回答不繼續: 1. 系統停止設定目的目錄動作,並關閉同

    步清單。  6d.  若目的端在 Server上,且無法連線: 

    1. 系統顯示網路斷線訊息,並詢問使用者是否重試。 

    2. 使用者回應系統。 2a.  若使用者選擇取消。 

    1. 紀錄錯誤訊息,並寫入同步紀錄檔中。2. 系統停止顯示同步清單。 

                  2b.  若使用者選擇重試。                       1a.  若網路恢復正常。 

    1. 系統開始模擬同步結果。                       1b.  若網路持續異常。 

    1. 回到 6d Step 1。  7a.  模擬結果沒有任何檔案更動。 

    1. 系統顯示無任何異常更動。  7b.  來源或目的檔案異常: 

    1. 系統顯示異常訊息。 1a.  檔案名稱太長。 

    1. 顯示檔案名稱太長無法複製到目的的錯誤訊息。 

                  1b.  磁碟空間不足。 1. 顯示檔案名稱太大的錯誤訊息。 

                  1c.  檔案操作檔案的權限不足。 1. 顯示檔案操作不足的錯誤訊息。 

                  1d.  檔案被系統鎖住。 1. 顯示檔案被系統鎖住的錯誤訊息。 

                  1e.  無法取得檔案時間。 1. 顯示檔案時間無法取得。 

     8a.  使用者想同步所有檔案: 

    1. 跳至 Step 10.  10a.  使用者選擇取消同步: 

  •  

     

    1. 系統關閉同步清單。  11a.  來源或目的磁碟機容量不足: 

    1. 系統將正在同步的檔案恢復成同步前的檔案。 2. 系統顯示磁碟空間太少訊息,並詢問使用者是

    否清理磁碟後再繼續同步。 3. 使用者回答系統。 

    3a.    使用者選擇停止同步: 1. 系統刪除同步暫存檔。 2. 系統紀錄錯誤訊息,並停止同步。 

                  3b.    使用者清除空間後,並選擇繼續同步: 1. 系統確認空間足夠後繼續同步。 2. 中斷的檔案沒有損毀。 

     11b.  網路斷線: 

    1. 系統將正在同步的檔案恢復成同步前的檔案。 2. 系統顯示網路斷線訊息,並詢問使用者是否重

    試。 3. 使用者回應系統。 

    3a.  使用者選擇取消: 1. 系統紀錄錯誤訊息,並寫入同步紀錄

    檔中。 2. 系統停止同步動作,紀錄停止時間,

    並寫入同步紀錄檔中。 3b.  若網路恢復正常,並且使用者選擇確定: 

    1. 系統從中斷的檔案繼續開始同步檔案。 

     11c.  若來源檔案異常(e.g.  檔名太長無法複製到目的): 

    1. 系統將正在同步的檔案恢復成同步前的檔案。 2. 系統紀錄錯誤訊息,並寫入同步紀錄檔中。 3. 系統繼續同步下一個檔案。 

     11d.  若目的檔案異常(e.g.  磁區為 FAT32  無法寫入大於

    4GB的檔案): 1. 系統將正在同步的檔案恢復成同步前的檔案。 2. 系統紀錄錯誤訊息,並寫入同步紀錄檔中。 3. 系統繼續同步下一個檔案。 

  •  

     

    10 

    Special Requirements 

    1. Synchronize List必須讓使用者一眼就能辨認出哪些檔案是將被刪除與將被覆蓋。 

    2. 執行完畢後不會鎖住檔案造成磁碟機無法卸載。 3. 來源與目的電腦時間不同步,仍然可以正確地同步

    檔案。 4. 同步時系統被中斷(e.g.電腦當機、電源中斷或

    SyncFree被刪除),不會造成檔案複製不完整或同步資料錯誤。 

    5. 同步時發生非預期例外(e.g. OutOfMemoryException, TooManyOpenFilesException),不會造成檔案複製不完整或同步資料錯誤。 

    6. 同步檔案時若有唯讀的檔案或檔案被系統鎖住,不會造成同步資料錯誤。 

    7. 同步檔案時若使用者操作檔案的權限不足,不會造成同步資料錯誤。 

    Technology And Data Variations List 

    None. 

    Frequency of Occurrence 

    時常發生  (若確實定期備份) 

    Miscellaneous  防止使用者在同步時不留意而將檔案刪除的保護機制,並增加同步檔案過程的強健度。 

  •  

     

    11 

    Use Case ID  UC‐02 Use Case Name  Manage Deleted Backup File (管理刪除備份檔案) Scope  SyncFree application Level  User goal Primary Actor  User Stakeholders & Interests 

    User:欲在同步後能備份刪除或被覆蓋的檔案。 

    Preconditions  執行過一次以上同步設定 Postcondition  SyncFree依照使用者所做的管理正常執行 Main Success Scenario 

    1. 使用者選擇 Synchronize Profile的功能列表。 2. 系統顯示 Synchronize Profile 的資訊。 3. 使用者執行同步。 4. 系統備份將被刪除或覆蓋的檔案,並同步檔案。 5. 使用者執行管理刪除備份檔案功能。 6. 系統開啟管理刪除備份檔案畫面。 7. 系統讀取備份檔案路徑,並顯示所有的備份檔案版

    本。 8. 使用者選擇確定,離開管理刪除備份檔案畫面。 

    Extensions  4a.    若無刪除或覆蓋的檔案:         1.    系統不備份檔案。         2.    系統直接同步檔案。  4b.    若備份版本個數超過使用者設定個數:         1.    系統刪除最早的備份版本。  7a.    如果從來沒刪除或覆蓋任何檔案: 

    1. 系統沒有顯示任何版本分類。 2. 移至 Step 6。 

     8a.    刪除檔案: 

    1. 使用者選擇其中一個備份檔案版本。 2. 系統顯示該版本內的所有刪除檔案名稱與被刪

    除時間資訊。 3. 使用者選擇要被刪除的檔案,並執行刪除。 4. 系統刪除使用者所選擇的檔案。 

     8b.    瀏覽檔案: 

  •  

     

    12 

    1. 使用者選擇檔案,並按下瀏覽。 2. 系統開啟使用者所選擇的檔案。 3. 使用者停止瀏覽檔案。 

     8c.    復原檔案: 

    1. 使用者選擇檔案,並選擇復原。 2. 系統將使用者所選擇的檔案移到 Source或

    Destination。 2a. 若 Source或 Destination已存在相同名稱的

    檔案: 1. 系統詢問使用者是否要復原。 2. 使用者回應系統 

    2a. 使用者選擇確定 1. 系統在備份路徑建立現在日期

    的備份版本。 2. 系統將 Source或Destination的檔

    案移置資料夾,將欲復原的檔

    案移至 Source或 Destination。2b. 使用者選擇否定: 

    1. 系統取消檔案復原。  8d.    清除檔案: 

    1. 使用者選擇清除。 2. 系統清除全部的備份檔案。 

    Special Requirements 

    1. 確保每一次同步時,都能啟用備份功能。 2. 管理操作介面直覺、易操作。 3. 被刪除的檔案應確保沒有損毀。 

    Technology And Data Variations List 

    None. 

    Frequency of Occurrence 

    偶爾發生  (通常同步多次後或檔案遺失後才會用到) 

    Miscellaneous  原功能殘破不堪,且無法備份 

  •  

     

    13 

    Brief Use Case 

    圖 3 Use Case Diagram (Brief) Actor  Goal  Brief 

    User  新增同步設定 使用者在新增同步設定精靈上輸入來源目錄、目地

    目錄、同步時間、同步策略以及名稱等資料,即可

    新增同步設定。 

    User  修改同步設定 使用者在已經設置的同步設定上,執行修改同步設

    定精靈修改同步設定,即可將同步設定檔案更新。

    User  刪除同步設定 使用者在已經設置好的同步設定上,執行刪除同步

    設定功能,即可將同步設定檔案刪除。 

  •  

     

    14 

    1.6 Non‐functional Requirements and Constraints 

     NFR ID  Category  Description NFR‐01  Usability  提供使用者方便介面管理同步 

    Scenario:  使用者在新增同步設定時相當輕鬆,每步設定都只要點一下就能完成。 

    NFR‐02  Performance 存取同步設定的設定檔案 IO時間不能超過三秒 

    Scenario:  使用者在執行同步設定時,SyncFree取得上次使用者所設定的同步資料時間不會超過三秒鐘。 

    NFR‐03  Reliability  檔案不因環境損毀 Scenario:  使用者在執行同步過程中,若發生網路斷線或當機,SyncFree 仍然保持同步檔案之正確性。 

    NFR‐04  Reliability  同步 20萬個檔案不損毀 Scenario:  使用者使用  SyncFree  同步 20萬個檔案不會造成檔案複製不完整或同步資料錯誤。 

    NFR‐05  Reliability  傳遞 50G 檔案不損毀 Scenario:  使用者使用  SyncFree  同步單一檔案大小為  50G  的檔案不會造成檔案複製不完整或同步資料錯誤。 

    NFR‐06  Scalability  擴充可傳遞 50G檔案 Scenario:  使用者原本使用  SyncFree  同步單一檔案大小只能為 640MB,當 單一檔案大小增加為 50G  時不會造成系統同步錯誤。 

    NFR‐07  Portability  支援多個作業系統 Scenario:  使用者可可在Windows與 Linux 平台中執行 SyncFree 

    NFR‐08  Testability  Code Coverage至少 85%以上 Scenario:  開發人員/測試人員在撰寫單元測試時,測試程式的覆蓋率至少百分之八十五以上。 

    NFR‐09  Supportability  支援多國語言檔案 Scenario:  使用者在使用 SyncFree時,可以同步泰文名稱的檔案。 

    NFR‐10  Modifiability  物件導向設計開發 Scenario:  開發人員在開發程式時,所有程式皆使用物件導向程式設計的方式製作成物件來開發,以增加維護效率。 

    3                                                        由於本專案的目的是增強  SyncFree的 Robustness,因此非功能性需求寫的比較詳細。 

  •  

     

    15 

    NFR‐11  Modifiability  可擴充同步策略 

    Scenario:  開發者界定良好的 Interface 以處理多種同步策略(簡單複製、精確複製、同步)。 

    NFR‐12  Modifiability  可擴充通訊協定 Scenario:  開發者界定良好的 Interface 以負責處理 File、HTTP與 FTP等通訊協定。 

  •  

     

    16 

    1.7 Glossary   

    Term  Definition or Description 同步 (Synchronize) 

    SyncFree Application  中三種複製檔案策略的其中一種。 此種策略會比較來源端目錄與目的端目錄中的所有檔案與

    目錄,並將那些修改時間最新的檔案複製到來源端目錄或

    目的端目錄。並會維持來源端檔案目錄與目的端檔案目錄

    的一致性。 同步方法 同步策略 (Synchronize Strategy) 

    也就是  SyncFree Application  中的複製檔案策略。 SyncFree Application  共有四種複製檔案策略: 1.  簡單複製 2.  精確複製 3.  同步 4.  移動 

    同步設定 (Synchronize Profile) 

    表設定複製檔案策略相關參數的動作。 這些參數包含有: 1.  來源端目錄 2.  使用的通訊協定 3.  目的端目錄 4.  同步策略

    資料過濾器 (Data Filter) 

    也就是檔案過濾器。 SyncFree  使用者可以設定欲同步的檔案。 e.g. “*.pdf” SyncFree只會同步那些附屬檔名為 pdf的檔案。

    精確複製 (Exact Copy) 

    SyncFree Application  中四種複製檔案策略的其中一種。 此種策略會比較來源端目錄與目的端目錄中的所有檔案與

    目錄,並將那些檔案名稱相同但檔案修改時間來源端檔案

    比目的端檔案新的檔案複製到目的端目錄。將那些目的端

    目錄中沒有的檔案或目錄刪除。 簡單複製 (Simply Copy) 

    SyncFree Application  中四種複製檔案策略的其中一種。 此種策略會比較來源端目錄與目的端目錄中的所有檔案與

    目錄,並將那些檔案名稱相同但檔案修改時間不同的檔案

    複製到目的端目錄。卻不將那些目的端目錄中沒有的檔案

    或目錄刪除。 同步策略  ‐  同步 (Synchronize of Synchronize 

    SyncFree Application  中四種複製檔案策略的其中一種。 若尚未備份過時,將只存在於來源端或目的端的檔案複製

    到對方目錄,而檔案已存在時,將最後修改時間較大者覆

  •  

     

    17 

    Strategy)  蓋最後修改時間較小者。 若已經備份過時,檔案只存在於來源端或目的端中任一方

    則比對上次同步記錄(Snapshot),若存在表示上次同步後檔案被使用者刪除,就刪除檔案;反之表示此檔為使用者新

    加入的檔案,就複製該檔案。 移動策略 (Movement) 

    SyncFree Application  中四種複製檔案策略的其中一種。 此種策略會將來源端目錄與檔案全部備份到目的端目錄,

    並在備份後刪除檔案,就像是在移動檔案。 強健度 (Robustness) 

    強健度是一種評估能承受的壓力或發生環境、程序改變的

    因應策略的品質。 系統或設計能被稱為“強健”,表示當發生可預期或不可預期的異常時,它能夠很好地處理使其造成最小的破壞而不

    喪失功能。 例外處理 (Exception Handling) 

    一種程式語言結構的機制設計,用特殊條件改變的正常程

    式執行的 Flow來處理例外發生。 

    例外處理模型 (Exception Handling Pattern) 

    例外處理的樣式。對於例外處理不熟悉的開發人員而言,

    他們不容易知道如何將重構方式套用在所需處理的例外程

    式上,因此在陳建村等人的研究中則以例外處理的重構為

    出發點,歸納出六種軟體開發人員在處理例外時,程式碼

    中經常出現的例外處理程式問題。也於例外處理設計中提

    出如何透過重構方法移除這些問題,使得開發人員可以套

    用這些重構方法到系統中,來提升強健度等級。 來源端 (Source) 

    同步時資料來源的資料夾。 

    目的端 (Destination) 

    同步時欲備份存放的資料夾。 

    SSL  Secure Sockets Layer是為網路通信提供安全及數據完整性的一種安全協議。在傳輸層對網路連接進行加密。 

    超文字傳輸協定 (HTTP) 

    全名為 HyperText Transfer Protocol,是網際網路上應用最為廣泛的一種網路協議。所有的WWW檔案都必須遵守這個標準。設計 HTTP最初的目的是為了提供一種發布和接收HTML頁面的方法。 

    檔案傳輸協定 (FTP) 

    全名為 File Transfer Protocol,是用於在網路上進行檔案傳輸的一套標準協議。它屬於網路協定組的應用層。 FTP是一個 8位的客戶端‐伺服器協定,能操作任何型式的檔案而不需要進一步處理,就像MIME或 Unicode 一樣。但是,FTP 有著極高的延時,這意味著,從開始請求到第一

  •  

     

    18 

    次接收需求資料之間的時間,會非常長;並且不時的必須

    執行一些冗長的登陸行程。 

    Copy  單純複製檔案的動作。 

    Backup  複製將被刪除的檔案的動作。 

    1.8 Software Environments   

    為了追求跨平台而不受作業系統限制,SyncFree使用 Java Language撰寫。 

  •  

     

    19 

    2 Domain class model4 

    Previous domain model 

     

    圖 4 Previous domain model  

                                                           這是之前學長寫的 Conceptual Diagram以供助教參考,學長的重點在 Profile的管理與 Log的記錄,但這些現在都不是本專案的重點。 

  •  

     

    20 

     

    2.1 Domain class diagram showing only concepts 

    首先,找出學長所撰寫的 Conception Model 的 Class: 

     

    再找出 Use Case 01、Use Case 02與 Use Case 03所出現的 Concepts: 

      

  •  

     

    21 

     Attribute: 

     

    以上皆可使用 Attribute 代替 Class。  Roles: 

     User使用者角色,不適合在 Domain Model呈現。 Sync Scheduler 排程不在本次專案的重點項目,因此省略。  Implement Construction : 

     Exception Handling為建構系統的架構。  Redundant: 

     MainWin為 View,不適合在 Domain Model呈現。 SyncFree不需要Watcher監視。 DeletedList  可使用 FolderList 取代。 DeletedFile可使用 FolderInfo取代。  

  •  

     

    22 

     本專案適合的 Classes: 

     

      

    中英文對照表 

    中文 英文 中文 英文

    同步時間 SyncTime  來源目錄  Source 最後修改時間 LastModifiedTime  目的目錄  Destination 例外處理 ExceptionHandling 同步方法  Strategy 同步資訊 SyncInfo  簡單複製策略  SimpleCopy 同步列表 SyncList  精確複製策略  ExactCopy 檔案備份 BackupManager  同步策略  Synchronize 同步記錄 Log  移動策略  Movement 同步記錄管理 LogManager  通訊協定 SyncService HTTP協定  HttpService  本地端  FileService Ftp協定  FtpService  錯誤訊息  Message 同步設定  Profile  刪除檔案版本  DeletedList 同步管理  SyncManager  同步設定管理  ProfileManager 檔案目的路徑  DestPath  檔案來源路徑  SourcePath 帳號  Account  同步設定名稱  Name 同步狀態  SyncState  密碼  Password 建立目錄狀態  MakeFolderState  複製檔案狀態  CopyFileState 刪除目錄狀態  RemoveFolderState 刪除檔案狀態  RemoveFileState 同步策略的選擇  StrategyChoice  協定的選擇  ProtocolChoice 備份路徑  BackupPath  刪除檔案  DeletedFile 備份版本個數  BackupVersion  檔案資訊  FolderInfo 檔案列表  FolderList     

  •  

     

    23 

    2.2 Add associations5 

    圖 5

                                                           Domain Model的繼承關係,這部份是參考課本 P.260,把它當成是 Super Set與 Sub Set。 

  •  

     

    24 

    Relationships ProfileManager管理 Profile。 SyncManager檢索 ProfileManager所管理的 Profile資訊。 SyncManager request SyncService to transfer file。 SyncManager產生 SyncList。 SyncManager  挑選 SyncStrategy (產生 SyncList的策略)。 LogManager管理 Log。 SyncService (當傳送資料發生例外)通知 LogManager。 SyncService 處理在傳送檔案的 SyncInfo。 SyncList  包含 SyncInfo。 SyncInfo被 SyncState來表達同步動作的狀態。 BackupManager備份將會被刪除的 SyncInfo。 BackupManager request SyncService to transfer file。 BackupManager管理 FolderList。 FolderList包含 FolderInfo。 SyncService 產生 FolderList ServiceSet是 FileService、HttpService與 FtpService的 SuperSet。 StrategySet是 SimpleCopy、ExactCopy、Synchronize 與Movement的 SuperSet。

  •  

     

    25 

     

    2.3 Add attributes 

    圖 6

  •  

     

    26 

    3 Class Diagram 

    3.1 Logical Architecture with UML package diagram 

     

    圖 7

    SyncFree以 Layer的架構構成,User Interface 提供使用者介面,Domain提供較檔案同步服務,Service提供檔案操作服務。上層會呼叫下層提供的服務,User Interface Package只會呼叫  Domain Package,Domain Package  只會呼叫 Technical Services Package。  

    但在這裡,我們使用 Exception Handling Constructor,使得 SyncFree下層的Package可以利用 Exception Handling Flow通知上層的 Package,在沒有直接的Coupling關係下,卻可以達到類似 Upcall的功能,提供非同步事件通知機制。 

  •  

     

    27 

     Package Diagram (Partial layered):

     圖 8

     GUI Package:SyncFree UI的相關類別。 Sync Package:同步管理與設定的相關類別。 Strategy:存放同步策略的相關類別。 Backup:存放備份刪除檔案的相關類別。 Manipulate:存放通訊協定服務的相關類別。  

  •  

     

    28 

     

    3.2 Use‐Case Realizations with GRASP Patterns 

    3.2.1  SSD 

    Synchronize Partial File (同步部份檔案)

    圖 9 Synchronize Partial File SSD 

  •  

     

    29 

      Manage Deleted Backup File (管理刪除備份檔案)6 

     圖 10 Manage Deleted Backup File SSD 

                                                           Use Case中"執行管理刪除備份檔案功能"等,僅針對 GUI操作,沒有用到 Domain Class,因此不列入 SSD中。 

  •  

     

    30 

     

    3.2.2  Contract 

    Contract01 ‐ listAllProfile: Operation:  listAllProfile() Cross Reference:  Use Case: UC‐01 Precondition:  None. Postconditions:  1. Profile instance profile was created (instance creation). 

    2. profile.name became name(attribute modification). 3. profile.source became source(attribute modification). 4. profile.destination became dest(attribute modification). 5. profile.protocol became protocol (attribute modification). 

    Contract02 ‐ selectProfile: Operation:  selectProfile(index: int) Cross Reference:  Use Case: UC‐01、UC‐02 

    Precondition:  1. ProfileList instance 已建立,且 List裡超過一筆以上的Profile instance。 

    2. 已取得使用者所選擇 Profile 的 index。 Postconditions:  1. 使用所選擇的 profile與 SyncManager產生關聯

    (associated formed). 2. 依據 profile.source 設置 SyncManager.source(attribute 

    modification). 3. 依據 profile.destination設置

    SyncManager.destination(attribute modification). 4. 依據 profile.protocol設置 SyncManager.protocol(attribute 

    modification). 5. SyncManager依據 protocol將產生 SyncService 的

    SubClass的 instance(instance creation). 

    Contract03 ‐ makeSyncList: Operation:  makeSyncList() Cross Reference:  Use Case: UC‐01 Precondition:  1. Profile 的 instance 已建立。 

    2. SyncManager的 source、destination與 protocol已由

  •  

     

    31 

    Profile 的設置完成。 

    Postconditions:  1. SyncService 的 SubClass instance已被建立(instance creation). 

    2. SyncList的 instance syncList已被建立(instance creation). 3. SyncInfo的 instance syncInfo已被建立(instance creation).4. syncInfo.sourcePath已被設置為 source 目錄底下的檔案

    路徑(attribute modification). 5. syncInfo.destPath已被設置為 destination目錄底下的檔

    案路徑(attribute modification). 6. syncInfo.syncState 依 SyncStrategy策略設置 instance 

    (attribute modification). 7. syncInfo加入至 SyncList中(associated formed). 

    Contract04 ‐ setIsSyncFile: Operation:  setDisSyncFile(disableList) Cross Reference:  Use Case: UC‐01 Precondition:  1. SyncList的 instance 已被建立. 

    2. SyncList內已包含一到多筆的 SyncInfo instance. 3. 已取得使用者所選擇的 SyncInfo的 index,與是否同步它.

    Postconditions:  已取得使用者所選擇的 SyncInfo的 instance syncInfo,並把syncInfo的 isSync設置為使用者所選擇的狀態(attribute modification). 

    Contract05 ‐ doSync: Operation:  doSync() Cross Reference:  Use Case: UC‐01 Precondition:  1. SyncList的 instance 已被建立. 

    2. SyncList內已包含一到多筆的 SyncInfo instance. 3. SyncService 的 instance 已建立  (FileService、FtpService、

    或 HttpService). 

    Postconditions:  1. SyncList內的 SyncInfo記錄的檔案已被複製或刪除(instance creation or instance deletion). 

    2. SyncList已被 SyncManager刪除(instance deletion).  Contract06 ‐ synchronize: Operation:  synchronize () Cross Reference:  Use Case: UC‐02 

  •  

     

    32 

    Precondition:  1. Profile 的 instance 已建立。 2. SyncManager的 source、destination與 protocol已由

    Profile 的設置完成。 

    Postconditions:  1. Profile 所設定的備份路徑內新增現在時間的資料夾(備份版本)。 

    2. Profile 所設定的路徑內的資料夾(備份版本),不會超過Profile 所設定的備份版本個數。 

    3. Profile 所設定的來源與目的路徑中將被刪除的檔案,已備份到 Profile所設定備份路徑中. 

    4. Profile 所設定的來源與目的路徑,以依照 Profile所設定的同步策略進行檔案的複製或刪除. 

  •  

     

    33 

     

    3.2.3  Sequence Diagram 

    listAllProfile: 

    圖 10 listAllProfile

    補充: SyncManager的 loadProfiles Method裡會讀所有 Profile 資料的存放檔案。這個檔案的位置是固定的,所以 loadProfiles裡會去產生一個 File 的區域變數,名為loadFile,loadFile 每讀一行資料產生一個 Profile,直到檔案 EOF為止。  Controller跟 Profile沒有關聯,還是可以產生 Profile,所以有 Low Coupling /High Cohesion。 

     

  •  

     

    34 

     selectProfile:

    圖 11 selectProfile

  •  

     

    35 

     makeSyncList:

    圖 12 makeSyncList SyncManager擁有 SyncStrategy 的資料,因此可直接對 SyncStrategy做產生 List的命令  (Information Expert)。 SyncManager與 SyncInfo沒有關係,在對 SyncStrategy 做 doCreateSyncList時,卻可以產生 SyncInfo (Low Coupling/High Cohesion)。 SyncStategy與 SyncInfo沒有關係,在對 SyncList做 add時,卻可以產生 SyncInfo (Low Coupling/High Cohesion)。 

      

  •  

     

    36 

     setDisSyncFile:

  •  

     

    37 

    圖 13 doSync:

    圖 14

    由於 SyncService 是只是介面,instance 是在 Runtime產生的,所以在做doCopyFile、doMakeDir、doDeleteFile 與 doRemoveDir是 Low Coupling。 此外,SyncService 是介面,不同的 SubClass 會以不同的方法實作,使得doCopyFile、doMakeDir、doDeleteFile 與 doRemoveDir的功能集中所以是 High Cohesion 的。 

  •  

     

    38 

    synchronize:7

    圖 15 synchronize

                                                           參考課本 P.236畫法,因 Synchronize Method會 Call makeSyncList、doBackup與 doSync三個Method,每個 Method都相當龐大也、可獨立,且 makeSyncList與 doSync上個 Iteration已分析過。因此使用 Reference來參照。 

  •  

     

    39 

    圖 16 DoBackup

  •  

     

    40 

    圖 17 MakeSyncList

    圖 18 DoSync

  •  

     

    41 

    3.2.4  Design Class Model 

    圖 19 Class Model 

  •  

     

    42 

    4 Implementation Class Model 

    4.1 Implementation Class Diagram 

     圖 20 SyncService 

     

     圖 21 SyncStrategy 

     

     圖 22 BackupViewer 

  •  

     

    43 

     

     圖 23 Domain 

  •  

     

    44 

     

    4.2 Implementation class model and design class model 

    difference 

    Class  Method  Design  Imp. 

    SyncState  None  yes  yes 

    synchronize  yes  yes doSync  yes  yes setDisSyncFile  yes  yes makeSyncList  yes  yes listAllProfile  yes  yes getSourceFolder  No  yes getDestFolder  No  yes getSyncInfos  No  yes 

    DomainController 

    optionDialog  No  yes 

    doSync  yes  yes setDisSyncFile  yes  yes makeSyncList  yes  yes createStrategy  yes  No setSyncSetting  yes  No doCopyFile  Yes  Yes doMkdir  Yes  Yes doDeletefile  Yes  Yes doDeleteFolder  Yes  Yes getSourceFolder  No  Yes getDestFolder  No  Yes getSyncInfos  No  Yes 

    SyncManager 

    makeFolderList  No  Yes 

    loadProfiles  Yes  Yes createProfile  Yes  Yes 

    saveProfile  No  Yes renameProfile  No  Yes 

    ProfileManager 

    deleteProfile  No  Yes 

  •  

     

    45 

    getProfile  Yes  Yes isProfileDirExist  No  Yes 

    getName  Yes  Yes setName  Yes  Yes getSyncTime  Yes  Yes setSyncTime  Yes  Yes getSource  Yes  Yes setSource  Yes  Yes getDestination  Yes  Yes setDestination  Yes  Yes getSyncMode  Yes  Yes setSyncMode  Yes  Yes getProtocol  Yes  Yes setProtocol  Yes  Yes getHostName  No  Yes setHost  No  Yes getPort  No  Yes setPort  No  Yes getAccount  Yes  Yes setAccount  Yes  Yes getPasswd  Yes  Yes setPasswd  Yes  Yes 

    getBackupPath  Yes  Yes 

    setBackupPath  Yes  Yes 

    getBackupVersion  Yes  Yes 

    Profile 

    setBackupVersion  Yes  Yes 

    setSyncState  Yes  Yes getSyncState  Yes  Yes setSourcePath  Yes  Yes getSourcePath  Yes  Yes setDestPath  Yes  Yes getDestFullPath  Yes  Yes getSourceProtocol  No  Yes getDestProtocol  No  Yes setSync  Yes  Yes 

    SyncInfo 

    isSync  Yes  Yes 

  •  

     

    46 

    equals  No  Yes 

    add  Yes  Yes getSyncInfos  No  Yes getSize  No  Yes containsSyncInfo  No  Yes 

    SyncList 

    setIsSyncFile  Yes  Yes 

    createFile  No  Yes createFtp  No  Yes createHttp  No  Yes isFolderExist  No  Yes doMakeDir  Yes  Yes doCopyFile  Yes  Yes doRemoveFile  Yes  Yes doRemoveDir  Yes  Yes 

    SyncService 

    getFolderList  No  Yes 

    FileService  doGetFolderList  No  Yes 

    ConnectToFTPserver  No  Yes DoFolderList  No  Yes 

    FtpServcie 

    retrieveFile  No  Yes 

    HttpServcie  CopyFile  No  Yes 

    createSimpleCopy  No  Yes createExactCopy  No  Yes createMove  No  Yes createSynchronize  No  Yes doCreateSyncList  Yes  Yes doCopy  No  Yes 

    SyncStrategy 

    doDelete  No  Yes 

    SimpleCopyStrategy  SimpleCopy  No  Yes 

    ExactCopyStrategy  doSimpleCopy  No  Yes 

    MovementStrategy  None  None  None 

    Synchronize  No  Yes SynchronizeStrategy doSynchronize  No  Yes 

    add  Yes  Yes getFolderListInfo  Yes  Yes 

    FolderList 

    KeyGenerate  No  Yes 

    FolderInfo  getLastModified  Yes  Yes 

  •  

     

    47 

    setLastModified  Yes  Yes getFileSize  No  Yes setFileSize  No  Yes getAbsolutePath  Yes  Yes setAbsolutePath  Yes  Yes setType  No  Yes getType  No  Yes setName  No  Yes getName  No  Yes setCanonicalPath  No  Yes getCanonicalPath  No  Yes 

    doBackup  Yes  Yes createBackupFoler  Yes  Yes checkBackupTimes  Yes  Yes deleteBackupFolder  No  Yes doBackupfile  No  Yes doBackupfile_FTP  No  Yes 

    BackupManager 

    doBackupfile_HTTP  No  Yes 

     Summary of implementation class/method changed 

    Iteration 1: 

      Number of added  Number of removed 

    Number of modified 

    Class  6  1  0 Method  43  2  0 

    HttpsService 實作困難被刪除 新增 SyncListViewer 與其相關 UI 元件  Iteration 2: 

      Number of added  Number of removed 

    Number of modified 

    Class  2  0  0 Method  21  0  0 

     

  •  

     

    48 

     

    4.3 Calculate Line of Code 

    No  Class Name  Number of methods 

    Line of Code in Class 

    1  SyncListViewer  8  245 2  CheckNodeRenderer  5  146 3  TreeLabel  6  80 4  NodeSelectionListener  3  69 5  CheckNode  18  149 6  SyncListPresentation  11  259 7  DomainController  10 (+1)  162 (+44) 8  SyncManager  13  300 9  SyncState  0  9 10  SyncInfo  13  97 11  SyncList  7  72 12  Profile  21 (+4)  143 (+12) 13  ProfileManager  8  200 14  SyncStrategy  8  62 15  SimpleCopyStrategy  3  44 16  ExcactCopyStrategy  3  58 17  SynchronizeStrategy  4  52 18  MovementStrategy  2  69 19  SyncServcie  9  29 20  FileServcie  9  140 21  FtpService  10 (+1)  161 (+22) 22  HttpServcie  9  223 23  FoderList  5  38 24  FoderInfo  16  101 25  BackupManager (new)  10  338 26  BackupViewer (new)  6  130   Total  223  3454 

  •  

     

    49 

     

    5 Programming 

    5.1 Snapshots of system execution 

     ListAllProfile

    圖 24  列出所有同步設定

    MakeSyncList 

    圖 25  選擇同步設定

  •  

     

    50 

      SetIsSyncFile 

    圖 26模擬同步列表

    DoSync 

    圖 27  執行同步列表

  •  

     

    51 

      來源或目的磁碟機容量不足 

    圖 28  硬碟空間不足行為重試

    網路斷線

    圖 29  網路斷線行為重試

  •  

     

    52 

    圖 30  開啟備份瀏覽視窗

    圖 31  檢視備份檔案

  •  

     

    53 

     

    5.2 Source Code Listing 

    SyncListViewer.java 

    package ntut.csie.SyncFree.GUI; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTree; import javax.swing.UIManager; import javax.swing.tree.TreeSelectionModel; import ntut.csie.SyncFree.domain.sync.SyncListPresentation; /** * 模擬同步結果視窗 */ public class SyncListViewer extends JDialog { /** Tree Panel */ private JTree sourceTree; private JTree destTree; /** Root Node */ private CheckNode sourceRoot; private CheckNode destRoot; /** 離開的Action */ private ActionListener exitAction, syncAction; /** Default Color */ private Color REMOVE_COLOR = Color.PINK; private Color CREATE_COLOR = new Color(144, 238, 144); private Color EXCEPTION_COLOR = new Color(153, 153, 204); /** Presentation */ private SyncListPresentation presentation; /** * Construct * @param mainWin * @param presentation */ public SyncListViewer(MainWin mainWin, SyncListPresentation presentation) { super(mainWin); //取得資料 this.presentation = presentation; this.sourceRoot = presentation.getSourceRoot(); this.destRoot = presentation.getDestRoot(); //初始化 this.initialize(); } /** 視窗 初始化 */ private void initialize() {

  •  

     

    54 

    //讓使用者無法點選Main View this.setModalityType(ModalityType.APPLICATION_MODAL); //設置Action this.setActionListener(); //建置Viewer this.createViewer(); //視窗固定大小 this.setSize(700, 500); //視窗位置置中 this.setLocationRelativeTo(null); this.setName("SyncListViewer"); this.setTitle("Simulate Synchronize List"); } /** * 建置Viewer */ private void createViewer() { /* 顏色說明Panel */ JPanel colorPanel = createColorPanel(); getContentPane().add(colorPanel, BorderLayout.NORTH); /* 按鈕Panel */ JPanel buttonPane = createButtonPanel(); getContentPane().add(buttonPane, BorderLayout.SOUTH); /* 在Tree旁放置空白Panel,空間配置 */ JPanel panel = new JPanel(new BorderLayout()); getContentPane().add(panel, BorderLayout.EAST); /* Source Tree */ sourceTree = createTree(sourceRoot); JScrollPane leftSP = new JScrollPane(sourceTree); /* Destination Tree */ destTree = createTree(destRoot); JScrollPane rightSP = new JScrollPane(destTree); /* Split Panel */ JSplitPane splitPane = createJSplitPane(0.5, JSplitPane.HORIZONTAL_SPLIT, null); splitPane.setLeftComponent(leftSP); JLabel sourceLabel = new JLabel("來源目錄同步後結果預覽:"); leftSP.setColumnHeaderView(sourceLabel); splitPane.setRightComponent(rightSP); JLabel destLabel = new JLabel("目的目錄同步後結果預覽:"); rightSP.setColumnHeaderView(destLabel); getContentPane().add(splitPane, BorderLayout.CENTER); } /** * 設置Action */ private void setActionListener() { addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ presentation.setIsSync(false); dispose(); } } ); exitAction = new ActionListener(){ public void actionPerformed(ActionEvent e) { presentation.setIsSync(false); dispose(); } }; syncAction = new ActionListener(){ public void actionPerformed(ActionEvent e) { //同步檔案 presentation.setIsSync(true);

  •  

     

    55 

    //設置 presentation.setDisSyncFile(); //關閉視窗 dispose(); } }; } /** * 建置Tree Panel * @param file * @return */ private JTree createTree(CheckNode rootNode) { //產生Tree JTree tree = new JTree(rootNode); //展開全部節點 for (int i=0; i < rootNode.getChildTotalCount(); i++) tree.expandRow(i); //設置Tree的背景顏色 Color background = UIManager.getColor("Panel.background"); tree.setBackground(new Color(background.getRGB())); //設置Tree的顯示動作設定 tree.setCellRenderer(new CheckNodeRenderer()); //設置Tree的滑鼠動作 tree.addMouseListener(new NodeSelectionListener(tree)); tree.setToggleClickCount(-2); //設置為單選 tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); //設置Tree的每個列高固定長度(0:auto size) tree.setRowHeight(0); return tree; } /** * 產生Button的Panel * @return */ private JPanel createButtonPanel() { JPanel buttonPane = new JPanel(); buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); { JButton okButton = new JButton("同步"); okButton.addActionListener(syncAction); okButton.setActionCommand("OK"); buttonPane.add(okButton); getRootPane().setDefaultButton(okButton); } { JButton cancelButton = new JButton("確定"); cancelButton.addActionListener(exitAction); cancelButton.setActionCommand("Cancel"); buttonPane.add(cancelButton); } return buttonPane; } /** * 同步顏色說明 * @return */ private JPanel createColorPanel() { JPanel colorPanel = new JPanel(); colorPanel.setLayout(new FlowLayout(FlowLayout.CENTER)); JLabel name = new JLabel("同步顏色說明:"); JLabel cover = new JLabel("同步後被覆蓋"); cover.setBackground(Color.white); cover.setOpaque(true); JLabel remove = new JLabel("同步後被刪除");

  •  

     

    56 

    remove.setBackground(REMOVE_COLOR); remove.setOpaque(true); JLabel create = new JLabel("同步時新增"); create.setBackground(CREATE_COLOR); create.setOpaque(true); JLabel exception = new JLabel("同步時會發生例外"); exception.setBackground(EXCEPTION_COLOR); exception.setOpaque(true); colorPanel.add(name); colorPanel.add(cover); colorPanel.add(remove); colorPanel.add(create); colorPanel.add(exception); return colorPanel; } /** * 產生一個JSplitPane物件 * @param resizeWeightValue 兩個切割Pane的比例 * @param orientation 方向 * @param rect 座標與寬高 * @return JSplitPane物件 */ private JSplitPane createJSplitPane(double resizeWeightValue, int orientation, Rectangle rect) { JSplitPane pane = new JSplitPane(); pane.setResizeWeight(resizeWeightValue); pane.setOrientation(orientation); if(rect != null) pane.setBounds(rect); return pane; } }  

  •  

     

    57 

     CheckNodeRenderer.java 

    package ntut.csie.SyncFree.GUI; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import javax.swing.Icon; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTree; import javax.swing.UIManager; import javax.swing.tree.TreeCellRenderer; class CheckNodeRenderer extends JPanel implements TreeCellRenderer { /** Default Color */ private Color BACK_COLOR = UIManager.getColor("Panel.background"); private Color REMOVE_COLOR = Color.PINK; private Color CREATE_COLOR = new Color(144, 238, 144); private Color EXCEPTION_COLOR = new Color(153, 153, 204); //樹的節點JPanel編輯,每個節點都是一個JPanel,JPanel中有一個JCheckBox和一個JLabel //TreeCellRenderer從DefaultMutableTreeNode(即CheckNode)中取得資料設置節點 //TreeCellRenderer主要設置Tree的外觀, protected JCheckBox check; protected TreeLabel label; public CheckNodeRenderer() { setLayout(null); add(check = new JCheckBox()); add(label = new TreeLabel()); check.setBackground(new Color(BACK_COLOR.getRGB())); check.setOpaque(true); label.setForeground(UIManager.getColor("Label.foreground")); } /** * 改寫每個Componet的顯示方式 */ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row, boolean hasFocus) { String stringValue = tree.convertValueToText(value, isSelected, expanded, leaf, row, hasFocus); setEnabled(tree.isEnabled()); CheckNode node = (CheckNode) value; check.setSelected(node.isSelected()); label.setFont(tree.getFont()); label.setText(stringValue); label.setSelected(isSelected); label.setFocus(hasFocus); //若為檔案,顯示檔案的Icon if (!node.isDirectory()) { if(node.getIcon() != null){ label.setIcon(node.getIcon()); }else{ label.setIcon(UIManager.getIcon("Tree.leafIcon")); } //若為資料夾則顯示資料夾的Icon } else if (!expanded || node.getChildCount() == 0) { label.setIcon(UIManager.getIcon("Tree.closedIcon")); } else { label.setIcon(UIManager.getIcon("Tree.openIcon")); }

  •  

     

    58 

    //將異動的檔案貼上顏色 switch (node.getChangeState()) { case FILE_CREATE: //Add File label.setBackground(CREATE_COLOR); break; case FILE_REMOVE: //Remove File label.setBackground(REMOVE_COLOR); break; case FILE_EXCEPTION: //Exception label.setBackground(EXCEPTION_COLOR); break; default: //Cover label.setBackground(BACK_COLOR); } //是否顯示CheckBox if (node.isShowCheckBox()) check.setVisible(true); else check.setVisible(false); return this; } /** * 獲取節點大小 */ public Dimension getPreferredSize() { Dimension d_check = check.getPreferredSize(); Dimension d_label = label.getPreferredSize(); if (!check.isVisible()) d_check.width = 0; return new Dimension(d_check.width + d_label.width, (d_check.height < d_label.height ? d_label.height : d_check.height)); } /** * 佈局CheckBox和Label */ public void doLayout() { Dimension d_check = check.getPreferredSize(); Dimension d_label = label.getPreferredSize(); if (!check.isVisible()) d_check.width = 0; int y_check = 0; int y_label = 0; if (d_check.height < d_label.height) { y_check = (d_label.height - d_check.height) / 2; } else { y_label = (d_check.height - d_label.height) / 2; } check.setLocation(0, y_check); check.setBounds(0, y_check, d_check.width, d_check.height); label.setLocation(d_check.width, y_label); label.setBounds(d_check.width, y_label, d_label.width, d_label.height); } /** * 設置背景顏色 */ public void setBackground(Color color) { super.setBackground(color); } }  

  •  

     

    59 

     TreeLabel.java 

    package ntut.csie.SyncFree.GUI; import java.awt.Color; public class TreeLabel extends JLabel { private Color backColor = UIManager.getColor("Panel.background"); private boolean isSelected; private boolean hasFocus; public TreeLabel() { } /** * 設置背景色 */ public void setBackground(Color color) { backColor = color; super.setBackground(color); } /** * 改寫Label的顯示方式 */ public void paint(Graphics g) { String str; if ((str = getText()) != null) { if (0 < str.length()) { if (isSelected) g.setColor(UIManager.getColor("Tree.selectionBackground")); else g.setColor(new Color(backColor.getRGB())); Dimension d = getPreferredSize(); int imageOffset = 0; Icon currentI = getIcon(); if (currentI != null) imageOffset = currentI.getIconWidth() + Math.max(0, getIconTextGap() - 1); g.fillRect(imageOffset, 0, d.width - 1 - imageOffset, d.height); } } super.paint(g); } /** * 取得Label的內容大小 */ public Dimension getPreferredSize() { Dimension retDimension = super.getPreferredSize(); if (retDimension != null) { retDimension = new Dimension(retDimension.width + 3, retDimension.height); } return retDimension; } /** * 設置是否被選擇 * @param isSelected */ public void setSelected(boolean isSelected) { this.isSelected = isSelected; } /** * 設置是否被Focus * @param hasFocus */ public void setFocus(boolean hasFocus) { this.hasFocus = hasFocus; } } 

  •  

     

    60 

     NodeSelectionListener.java 

    package ntut.csie.SyncFree.GUI; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Enumeration; import javax.swing.JTree; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; /** * 監聽點選CheckNode的事件 */ class NodeSelectionListener extends MouseAdapter { JTree tree; NodeSelectionListener(JTree tree) { this.tree = tree; } /** * 滑鼠點擊Node */ public void mouseClicked(MouseEvent e) { //得到被選的節點 int x = e.getX(); int y = e.getY(); int row = tree.getRowForLocation(x, y); TreePath path = tree.getPathForRow(row); //判斷是否按一下了節點 if (path != null) { //取得被按一下的節點 CheckNode node = (CheckNode) path.getLastPathComponent(); boolean isSelected = !(node.isSelected()); //設置被按一下的節點CheckBox,使其狀態與原來狀態相反 node.setSelected(isSelected); //如果被選節點不是葉節點,將其所有子節點設置為CheckBox狀態設成與其相同的狀態 if(!node.isLeaf()){ node.getNextNode(); //選擇/取消 子樹 selectSubTree(node, node.isSelected()); } //刷新樹(這步是必須的,否則將看不到上面所有設置的效果); ((DefaultTreeModel) tree.getModel()).nodeChanged(node); tree.revalidate(); tree.repaint(); } } /** * 選擇/取消 子樹 * @param node * @param isSeleted */ private void selectSubTree(CheckNode node, boolean isSeleted) { if(!node.isLeaf()) { Enumeration enuc= node.children(); while (enuc.hasMoreElements()) { CheckNode c = (CheckNode) enuc.nextElement(); c.setSelected(isSeleted); selectSubTree(c, isSeleted); } } } }  

  •  

     

    61 

     CheckNode.java 

    package ntut.csie.SyncFree.GUI; import javax.swing.Icon; import javax.swing.tree.DefaultMutableTreeNode; /** * 節點的資料 */ public class CheckNode extends DefaultMutableTreeNode { public static enum ChangeState {FILE_COVERED, FILE_REMOVE, FILE_CREATE, FILE_EXCEPTION}; /** 是否選取 */ private boolean isSelected = true; /** 是否為資料夾,否則為檔案 */ private boolean isDirectory = false; /** SyncList Vector Index */ private int index = -1; /** 是否顯示CheckBox */ private boolean isShowCheckBox = false; /** File Icon */ private Icon icon = null; /** Destination File Path */ private String filePath = ""; /** File Change Statement */ private ChangeState changeState = ChangeState.FILE_COVERED; /** * Construct */ public CheckNode() { this(null); } public CheckNode(Object userObject) { this(userObject, true, true); } public CheckNode(Object userObject, boolean allowsChildren, boolean isSelected) { super(userObject, allowsChildren); this.isSelected = isSelected; } /** * 是否選擇 * @return */ public boolean isSelected() { return isSelected; } public void setSelected(boolean isSelected) { this.isSelected = isSelected; } /** * File Icon * @param icon */ public Icon getIcon(){ return icon; } public void setIcon(Icon icon){ this.icon = icon; } /** * 0:Cover 1:Create 2:Remove 3:Exception * @return */ public ChangeState getChangeState() { return changeState; } /** * 0:Cover 1:Create 2:Remove 3:Exception

  •  

     

    62 

    * @param changeState */ public void setChangeState(ChangeState State) { this.changeState = State; } /** * 取得File的路徑 * 不曉得有沒有機會用到 * @return */ public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } /** * 是否為資料夾 * @return */ public boolean isDirectory() { return isDirectory; } public void setDirectory(boolean isDirectory) { this.isDirectory = isDirectory; } /** * 取得SyncList的Vector Index * @return */ public int getVectorIndex() { return index; } public void setVectorIndex(int index) { this.index = index; if (index != -1) setShowCheckBox(true); } /** * 設定是否要顯示CheckBox * @param showDialog */ public void setShowCheckBox(boolean showDialog) { isShowCheckBox = showDialog; //若要顯示CheckBox,將Parent的CheckBox改成可選擇 if (showDialog) { CheckNode parent = (CheckNode) getParent(); if (parent != null && !parent.isShowCheckBox()) parent.setShowCheckBox(true); } } public boolean isShowCheckBox() { return isShowCheckBox; } /** * 計算Tree的Row數量 * @param node * @return */ public int getChildTotalCount() { int count =1; for (int i=0; i < getChildCount(); i++) { CheckNode childNode = (CheckNode) getChildAt(i); count += childNode.getChildTotalCount(); } return count; } }

  •  

     

    63 

     SyncListPresentation.java 

    package ntut.csie.SyncFree.GUI; import java.io.File; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.TreeMap; import javax.swing.Icon; import javax.swing.filechooser.FileSystemView; import ntut.csie.SyncFree.domain.DomainController; public class SyncListPresentation { /** Root Node */ private CheckNode sourceRoot; private CheckNode destRoot; /** Folder Map */ private TreeMap sourceMap = new TreeMap(); private TreeMap destMap = new TreeMap(); /** File */ private File sourceFile; private File destFile; /** Domain */ private DomainController domainController; /** 是否同步 */ private boolean isSync; /** * Constructor * @param syncList * @param theProfile */ public SyncListPresentation(DomainController domain) { this.domainController = domain; this.sourceFile = new File(domain.getSourceFolder()); this.destFile = new File(domain.getDestFolder()); //輸入Tree的資料 this.inputData(); } /** * 輸入樹的資料 */ private void inputData() { sourceRoot = getFileRoot(sourceFile, sourceMap); inputFolderNode(sourceFile, sourceRoot, sourceMap); destRoot = getFileRoot(destFile, destMap); inputFolderNode(destFile, destRoot, destMap); transSyncOutput(); } /** * 設置Root * @param file * @param map * @return */ private CheckNode getFileRoot(File file, TreeMap map) { CheckNode rootNode = new CheckNode(file.getName()); rootNode.setFilePath(file.getPath()); rootNode.setDirectory(true); map.put(file.getPath(), rootNode);

  •  

     

    64 

    return rootNode; } /** * 輸入來源/目錄端內資料夾資訊 * @param parentFile * @param parentNode * @param folderMap */ private void inputFolderNode(File parentFile, CheckNode parentNode, TreeMap folderMap) { File[] childFile = parentFile.listFiles(); for (int i = 0; i < childFile.length; i++) { //只處理資料夾 if (childFile[i].isDirectory()) { CheckNode node = new CheckNode(childFile[i].getName()); node.setDirectory(true); //Trace Sub Folder inputFolderNode(childFile[i], node, folderMap); node.setFilePath(childFile[i].getPath()); parentNode.add(node); //輸入Folder資料至Map folderMap.put(childFile[i].getPath(), node); } } } /** * 將同步資訊轉成同步結果 */ private void transSyncOutput() { Enumeration iterator = domainController.getSyncInfos(); //Root路徑 String sourceRootPath = sourceFile.getPath().replace("\\", "/"); String destRootPath = destFile.getPath().replace("\\", "/"); SyncInfo theSyncInfo; String theSourceFullPath; String theDestFullPath; //Walk Around SyncList for (int index =0; iterator.hasMoreElements(); index++) { theSyncInfo = (SyncInfo) iterator.nextElement(); theSourceFullPath = theSyncInfo.getSourceFullName(); theDestFullPath = theSyncInfo.getDestFullName(); File syncSourceFile = new File(theSourceFullPath); File syncDestFile = new File(theDestFullPath); /* 來源或目的端的Tree Folder Data */ TreeMap treeMap; //判斷目的端目錄為Source或Destination if (theDestFullPath.startsWith(destRootPath)) treeMap = destMap; else treeMap = sourceMap; String reallyPath = theDestFullPath.replace("/", "\\"); String folderPath = reallyPath.substring(0, reallyPath.lastIndexOf(String.valueOf("\\"))); CheckNode node = new CheckNode(syncDestFile.getName()); CheckNode parentNode = treeMap.get(folderPath); switch (theSyncInfo.getSyncState()) { /* Make Folder */ case MK_DIR: CheckNode alreadyNode = treeMap.get(reallyPath); if (alreadyNode == null) {

  •  

     

    65 

    node.setDirectory(true); node.setChangeState(ChangeState.FILE_CREATE); } else { //如果已存在則修改該資料夾的狀態 node = null; alreadyNode.setChangeState(ChangeState.FILE_CREATE); alreadyNode.setVectorIndex(index); } break; /* Remove Folder */ case RM_DIR: //不需要新增Node,所以刪除Node node = null; //取得目錄,並將它標示為刪除 CheckNode targetNode = treeMap.get(reallyPath); targetNode.setChangeState(ChangeState.FILE_REMOVE); targetNode.setVectorIndex(index); break; /* Copy File */ case COPY_FILE: //取得來源端的Icon圖示(目的端的檔案可能還沒產生) Icon sourceIcon = FileSystemView.getFileSystemView().getSystemIcon(syncSourceFile); node.setIcon(sourceIcon); node.setDirectory(false); //若目的端檔案不存在,表示為新增的檔案 if (!syncDestFile.exists()) node.setChangeState(ChangeState.FILE_CREATE); else node.setChangeState(ChangeState.FILE_COVERED); break; /* Remove File */ case RM_FILE: //取得目的端的Icon圖示(來源端的檔案已被刪掉) Icon destIcon = FileSystemView.getFileSystemView().getSystemIcon(syncDestFile); node.setIcon(destIcon); node.setDirectory(false); node.setChangeState(ChangeState.FILE_REMOVE); break; } //若有新增Child Node(File),則加入至Parent Node(Folder) if (parentNode != null && node != null) { parentNode.add(node); treeMap.put(reallyPath, node); node.setVectorIndex(index); } } } /** * 設定不同步的檔案 */ public void setDisSyncFile() { //取消同步列表 List disSyncList = new ArrayList(); //取得不同步檔案清單 inputDisSyncList(sourceRoot, disSyncList); inputDisSyncList(destRoot, disSyncList); //設置不同步檔案 domainController.setDisSyncFile(disSyncList); } /** * 將Source / Destination的不同步的檔案記錄 * @param node * @param disSyncList */ public void inputDisSyncList(CheckNode node, List disSyncList) {

  •  

     

    66 

    if (!node.isSelected() && node.getVectorIndex() != -1) disSyncList.add(node.getVectorIndex()); for (int i=0; i < node.getChildCount(); i++) { CheckNode childNode = (CheckNode) node.getChildAt(i); inputDisSyncList(childNode, disSyncList); } } /** * 取得Source Tree Root * @return Source目錄的Root */ public CheckNode getSourceRoot() { return sourceRoot; } /** * 取得Destination Tree Root * @return Destination目錄的Root */ public CheckNode getDestRoot() { return destRoot; } /** * 設置是否同步檔案 * @param isSync */ public void setIsSync(boolean isSync) { this.isSync = isSync; } public boolean isDoSync() { return isSync; } }    

     

  •  

     

    67 

     DomainController.java 

    package ntut.csie.SyncFree.domain; import java.io.IOException; import java.util.Enumeration; import java.util.List; import java.util.logging.Logger; import javax.swing.JOptionPane; import nttu.csie.SyncFree.slefexception.FTPConnectException; import nttu.csie.SyncFree.slefexception.HttpConnectException; import nttu.csie.SyncFree.slefexception.SizeOutException; import ntut.csie.SyncFree.domain.sync.SyncManager; public class DomainController { //ProfileManager private ProfileManager profileManager = null; //SyncManager private SyncManager syncManager = null; //使用java logger記錄 private static Logger logger = null; private Profile selectProfile = null; /** * constructor */ public DomainController() { //create model profileManager = new ProfileManager(); logger = Logger.getLogger(this.toString()); } /** * ProfileList */ public void listAllProfiles() { profileManager.loadAllProfiles(); } /** * 選擇Profile * @param index */ public void selectProfile(int index) { Profile aProfile = profileManager.getProfile(index); selectProfile = aProfile; syncManager = new SyncManager(aProfile); } /** * 產生SyncList * @param aProfile */ public void makeSyncList(Profile aProfile) { int choice = 1; // 0 = 重試 , 1 = 取消 Object[] options = {"重試", "取消"}; //customize Dialog option的按鈕內容 //利用do-while來控制Retry的行為 do{ // 假如在MakeFolderList發生Exception就拋出:可能發生在Source or destination端 // 產生 FolderList try { if(syncManager.makeFolderList(aProfile.getSource(),aProfile.getDestination())) { // 產生 Sync List syncManager.makeSyncList(); } } catch (IOException e) { choice = optionDialog("同步檔案錯誤,請檢查網路連線是否正常","SyncFree",options); if(choice == 0)

  •  

     

    68 

    logger.info("Retry.........."); } catch (FTPConnectException e) { //當FTP網路異常時,拋出自己所定義的Exception choice = optionDialog("網路連線異常,請檢查FTP Server是否開啟或網路是否正常,並進行重試!","SyncFree",options); if(choice == 0) logger.info("Retry.........."); } catch (HttpConnectException e) { //當如果發生網路異常的例外時,可能影響的有makeFolderList()以及DoSync() //可進行重試的機制,若取消就整個放棄同步 choice = optionDialog("網路連線異常,請檢查Server是否開啟或網路是否正常並進行重試!","SyncFree",options); if(choice == 0) logger.info("Retry.........."); } } while(choice == 0); } /** * 執行同步 */ public void doSync() { int choice = 1; // 0 = 重試 , 1 = 取消 Object[] options = {"重試", "取消"}; //customize Dialog option的按鈕內容 //利用do-while來控制Retry的行為 do{ try { syncManager.doSync(); } catch (SizeOutException e) { //當發生磁碟空間不足時,在這層做處理 choice = optionDialog("同步發生磁碟空間不足,請清理磁碟後,按下重試進行重新同步或放棄同步","SyncFree",options); if(choice == 0) logger.info("Retry.........."); } catch (FTPConnectException e) { //當FTP網路異常時,拋出自己所定義的Exception choice = optionDialog("網路連線異常,請檢查FTP Server是否開啟或網路是否正常,並進行重試!","SyncFree",options); if(choice == 0) logger.info("Retry.........."); } catch (IOException e) { choice = optionDialog("同步檔案錯誤,請檢查網路連線是否正常","SyncFree",options); if(choice == 0) logger.info("Retry.........."); } }while(choice == 0); syncManager = null; } public void synchronize() { int choice = 1; // 0 = 重試 , 1 = 取消 Object[] options ={"重試","取消"}; //customize Dialog option的按鈕內容 do{ //利用do-while來控制Retry的行為 try { // 產生 Sync List SyncList syncList = syncManager.makeSyncList1(); BackupManager backupManager = new BackupManager(selectProfile, syncList); backupManager.DoBackup(); syncManager.doSync(); //當重試同步成功時,需將choice設成1,跳離重試迴圈 choice = 1; } catch(FileNotFoundException e) choice = optionDialog("檔案寫入錯誤,請檢查目的端檔案是否唯讀,並取消其唯讀後進行重試","SyncFree",options); if(choice == 0) m_logger.info("Retry.........."); } catch (FTPConnectException e) { //當FTP網路異常時,拋出自己所定義的Exception choice = optionDialog("網路連線異常,請檢查FTP Server是否開啟或網路是否正常,並進行重試!","SyncFree",options);

  •  

     

    69 

    if(choice == 0) m_logger.info("Retry.........."); } catch (SizeOutException e) { //當發生磁碟空間不足時,在這層做處理 choice = optionDialog("同步發生磁碟空間不足,請清理磁碟後,按下重試進行重新同步或放棄同步","SyncFree",options); if(choice == 0) m_logger.info("Retry.........."); } catch (IOException e) { choice = optionDialog("同步檔案錯誤,請檢查網路連線是否正常","SyncFree",options); if(choice == 0) m_logger.info("Retry.........."); } }while(choice == 0); syncManager = null; } /** * 設置不同步的檔案 * @param disSyncList */ public void setDisSyncFile(List disSyncList) { syncManager.setDisSyncFile(disSyncList); } /** * 取得來源與目的路徑 * @return Path */ public String getSourceFolder() { return syncManager.getSourceFolder(); } public String getDestFolder() { return syncManager.getSourceFolder(); } public Enumeration getSyncInfos() { return syncManager.getSyncInfos(); } /** * 跳出Dialog,提供使用者選擇 * @param content * @param title * @param options * @return */ public static int optionDialog(String content,String title,Object[] options){ int n = JOptionPane.showOptionDialog( null,content,"SyncFree", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, // icon options, options[0] // initial value ); return n; } }  

  •  

     

    70 

     SyncManager.java 

    package ntut.csie.SyncFree.domain.sync; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Enumeration; import java.util.List; import java.util.logging.Logger; import nttu.csie.SyncFree.slefexception.FTPConnectException; import nttu.csie.SyncFree.slefexception.HttpConnectException; import nttu.csie.SyncFree.slefexception.SizeOutException; import ntut.csie.SyncFree.domain.strategy.SyncStrategy; import ntut.csie.SyncFree.services.FolderList; import ntut.csie.SyncFree.services.SyncService; public class SyncManager { private String FILE_PROTOCOL = "file://"; private String FTP_PROTOCOL = "ftp://"; private String HTTP_PROTOCOL = "http://"; //Folder private String sourceFolder; private String destFolder; //Service private SyncService sourceService; private SyncService destService; //Folder List private FolderList sourceFolderList; private FolderList destFolderList; //StrategyMode private int strategyMode; //Protocol Name private String sourceProtocol; private String destProtocol; //SyncList private SyncList syncList; //logger private Logger logger; /** * 用 profile 為參數的 constructor */ public SyncManager(Profile aProfile){ sourceProtocol = "file://"; destProtocol = aProfile.getProtocol(); strategyMode = aProfile.getSyncMode(); sourceFolder = aProfile.getSource(); destFolder = aProfile.getDestination(); // create service instance if (aProfile.getProtocol().equals(FILE_PROTOCOL)){ destService = SyncService.createFile(); } else if(aProfile.getProtocol().equals(HTTP_PROTOCOL)){ destService = SyncService.createHttp(aProfile); } else if(aProfile.getProtocol().equals(FTP_PROTOCOL)){ destService = SyncService.createFtp(aProfile); } //來源端只能為本地端 sourceService = SyncService.createFile(); logger = Logger.getLogger(this.toString()); } /**

  •  

     

    71 

    * 產生 FolderList */ public boolean makeFolderList(String aSourceFolder, String aDestFolder) throws IOException, FTPConnectException, HttpConnectException{ boolean souceResult = false; boolean destResult = false; try { //產生Source與Destination Folder List souceResult = makeFolderList(aSourceFolder, sourceService, sourceFolderList); destResult = makeFolderList(aDestFolder, destService, destFolderList); } catch (FTPConnectException e) { throw e; } catch (HttpConnectException e) { throw e; } catch (IOException e) { throw e; } return (souceResult && destResult); } private boolean makeFolderList(String aSourceFolder, SyncService service, FolderList folderList) throws IOException, FTPConnectException, HttpConnectException { try { folderList = service.getFolderList(aSourceFolder); } catch (FTPConnectException e) { throw e; } catch (IOException e) { //將其assign的東西做clean,並準備進行重試 folderList = null; //將例外記錄下來 logger.info("Folder path invalid!"); throw new HttpConnectException(); } return true; } /** * 產生 SyncList */ public void makeSyncList() { SyncStrategy syncStrategy = null; switch (strategyMode) { //Simple Copy Strategy case 0: syncStrategy = SyncStrategy.createSimpleCopy(); break; //Exact Copy Strategy case 1: syncStrategy = SyncStrategy.createExactCopy(); break; //Synchronize Strategy case 2: syncStrategy = SyncStrategy.createSynchronize(); break; //Move Strategy case 3: syncStrategy = SyncStrategy.createMove(); break; } syncList = syncStrategy.doCreateSyncList(sourceProtocol, sourceFolder, destProtocol, destFolder, sourceFolderList, destFolderList); } /** * 執行同步 */ public void doSync() throws FileNotFoundException,FTPConnectException, SizeOutException,IOException{ SyncInfo theSyncInfo; //實際執行同步 Enumeration iterator = syncList.getSyncInfos().elements(); while(iterator.hasMoreElements()){ theSyncInfo = (SyncInfo) iterator.nextElement();

  •  

     

    72 

    //若不同步則跳至下一筆 if (!theSyncInfo.isSync()) continue; try { switch (theSyncInfo.getSyncState()){ case COPY_FILE: // copy file doCopyFile(theSyncInfo); break; case MAKE_DIR: // mkdir doMkdir(theSyncInfo); break; case REMOVE_FILE: //RmFile doDeletefile(theSyncInfo); break; case REMOVE_DIR: //RmDir doRmdir(theSyncInfo); break; } //利用拋出的exception來判斷使用者是否取消同步 } catch(FileNotFoundException e){ throw e; } catch (FTPConnectException e) { throw e; } catch (SizeOutException e) { throw e; }catch (IOException e) { //當如果發生網路異常的例外時,將例外往上傳遞 throw e; } } } /** * 執行 Copy File 的動作 * @param aSyncInfo * @throws FileNotFoundException * @throws FTPConnectException * @throws SizeOutException * @throws IOException */ private void doCopyFile(SyncInfo aSyncInfo) throws FileNotFoundException,FTPConnectException, SizeOutException,IOException{ String theSourceFullPath = aSyncInfo.getSourceFullName(); String theDestFullPath = aSyncInfo.getDestFullName(); //Source一定為File Protocol if (aSyncInfo.getSourceProtocol().equals(FILE_PROTOCOL)) { try { //File → All if(destService.doCopyFile(theSourceFullPath,theDestFullPath,true)) { logger.info("Copy " + theSourceFullPath + " to " + theDestFullPath ); } } catch (FileNotFoundException e) { throw e; } catch (FTPConnectException e) { //當FTP發生網路異常時,將自行定義的例外往上傳遞 throw e; } catch (SizeOutException e) { //因為檔案空間不足時會複製不完全,所以就該檔做清空做recovery,空出空間來 //假入Dest端是本地端,才做clean的動作 if(aSyncInfo.getDestProtocol().equals(FILE_PROTOCOL)){ File file = new File(theDestFullPath); if(file.exists()) file.delete(); } throw e; } catch (IOException e) { //當如果發生網路異常的例外時,將例外往上傳遞 throw e; } finally { logger.warning("Error: Copy "+ theSourceFullPath + " to " + theDestFullPath); }

  •  

     

    73 

    } } /** * 產生資料夾 * @param aSyncInfo */ private void doMkdir(SyncInfo aSyncInfo){ String theSourceFullPath = aSyncInfo.getSourceFullName(); String theDestFullPath = aSyncInfo.getDestFullName(); if (aSyncInfo.getSourceProtocol().equals(FILE_PROTOCOL)) { if(destService.doMakeDir(theSourceFullPath,theDestFullPath)) { logger.info("MkDir - " + theDestFullPath); } else{ logger.warning("Error: Mkdir - " + theDestFullPath); } } } /** * 刪除檔案 * @param aSyncInfo */ private void doDeletefile(SyncInfo aSyncInfo){ String theDestFullPath = aSyncInfo.getDestFullName(); if(aSyncInfo.getSourceProtocol().equals(FILE_PROTOCOL)) { if(destService.doRemoveFile(theDestFullPath)) { logger.info("Delete File - " + theDestFullPath ); } else { logger.warning("Error: Delete File - " + theDestFullPath); } } } /** * 刪除資料夾 * @param aSyncInfo */ private void doRmdir(SyncInfo aSyncInfo) { String theDestFullPath = aSyncInfo.getDestFullName(); if (aSyncInfo.getSourceProtocol().equals(FILE_PROTOCOL)) { if (destService.doRemoveDir(theDestFullPath,true)) { logger.info("RmDir - " + theDestFullPath ); } else { logger.warning("RmDir: Delete File - " + theDestFullPath); } } } /** * 取得來源與目的路徑 * @return */ public String getSourceFolder() { return sourceFolder; } public String getDestFolder() { return destFolder; } /** * 取得同步資訊 * @return */ public Enumeration getSyncInfos() { return syncList.getSyncInfos().elements(); } /** * 設置不同步檔案 * @param disSyncList */ public void setDisSyncFile(List disSyncList) { for (int i=0; i < disSyncList.size(); i++) { syncList.setIsSyncFile(disSyncList.get(i), false); } } } 

  •  

     

    74 

     SyncState.java 

    package ntut.csie.SyncFree.domain.sync; public enum SyncState { MK_DIR, //產生資料夾 COPY_FILE, //複製檔案 RM_FILE, //刪除檔案 RM_DIR; //刪除資料夾 }  SyncInfo.java 

    package ntut.csie.SyncFree.domain.sync; import java.util.StringTokenizer; public class SyncInfo { //同步動作 private SyncState syncState; //來源路徑 private String sourcePath; //目的路徑 private String destPath; //是否同步 private boolean isSync; //來源目的的檔案協定 private String sourceProtocol; private String destProtocol; public SyncInfo(SyncState aSyncState, String aSourceProtocol,String aSourceFullName, String aDestProtocol,String aDestFullName){ syncState = aSyncState; sourcePath = aSourceFullName; destPath = aDestFullName; sourceProtocol = aSourceProtocol; destProtocol = aDestProtocol; } /** * 同步動作 * @param syncState */ public void