走錯了很多路,最後在參考了Patterns of Enterprise Application Architecture
之後,總算是確定.
否則以我胡思亂想的個性,可能還在亂寫中(真的寫的很亂,慚愧…).
直接利用人家已經想好的 Pattern, 整個大方向會比較快確定下來.
大致的 Class diagram 就長這樣.
About Web Crawler
什麼是 Web Crawler?? 其實就是一般慣稱的 bot.
定期在網路上漫遊,將網頁的部分(全部)資料以及特徵值放到自身的資料庫裡面,做為 Search engine 的資料來源.
所以你大概會這麼想,使用一部或多部 server, 以 multiple thread 甚或 multiple process 的方式,去網路上漫遊.
當遇到網頁中有連結的時候,先排程到 Queue 裡面,讓其他有空的 thread/process 能從 Queue 裡面循序取出來,進行漫遊.
設計這樣的一個程式,所需要考慮的一些事情:
1. Multiple database server的考量:一個 database server 可能會承受不住這種情況,所以需要考慮 replication 或是更適合用來取資料的 database engine.
2. Multiple host + Multiple thread 的考量:網路上現今的資料量絕對超過你的想像,所以你不可能使用一台機器甚或一個 thread 去做漫遊,這樣會花費太多時間.另外也要考慮 lock, resource …等等的問題.
3. Queue 實作的方式: 有些立即性的連結,可能需要放在 thread/process 自身的 queue 裡面,比較不具立即性的連結,就可能放在 database server 上. (Priority Queues)
我想我考慮的可能還太淺.
其他一些可以參考的或可能用上的技術:
* Lucene
* Google Desktop search API
* MySQL replication
* Thread pool
* DDJ 2001 April: Web site searching & Indexing in Perl
MSN messenger+ foolbar2000
早先就知道可以讓 Windows Media Player 播放的曲目資訊顯示在 MSN messenger 上.
可是…我並不喜歡用 Windows Media Player 來播放歌曲啊.
之前也逛到讓 foolbar2000 播放的曲目資訊顯示在 MSN messenger 上的方法,可是我並沒有去試.
今天心血來潮了,上網搜尋了一下:
*PCDVD數位科技討論區 – [教學]如何不慣WMP9啟動MSN歌名同步
節錄步驟如下,照著作就對了.
1. 先到這裡 HKEY_CURRENT_USER\Software\Microsoft\MSNMessenger\PerPassportSettings000000000\
把 “ShowTransientPSM” 改成 dword:00000001.
2. 下載 MSN Messenger Now Playing Plugin (gen_msn), 解開以後,放到 foolbar2000 目錄下的 components.
3. 收工.
LPI模擬試題
前兩年考 LPI Level 1 的時候找到的模擬試題網站,對考試很有幫助.
- LPI – Exam Emulation
- LPI 101 Certification Practice Test
- LPI 102 Certification Practice Test
- ICE:ICE(home)
ICE 那個好像已經失效了,現在連結到 IBM 自己的認證中心…. @_@”
RFMaintainer 的 statechart (補)
昨天的RFMaintainer 的 statechart並沒有實作部份,今天補上…
你可以看到這些都是寫在 Form 裡面,並沒有跟 UI 切開….
這就是我疑問的地方…
不過對照圖之後,你會發現這樣的寫法很清楚…不會有補東牆挖西牆,東改一塊西改一塊的問題…
#region State Chart Implementation. private int _StateVariable = 0; private int _StateVariable_A = 0; private int _StateVariable_B = 0; private int _StateVariable_C = 0; ////// State 1: Transient /// private void go_state_1() { _StateVariable = 1; if( txtConnectString.Text == "" ) go_state_2(); else go_state_3(); } ////// State 2: Connection string null /// private void go_state_2() { _StateVariable = 2; // disable controls. radioButton1.Enabled = false; radioButton2.Enabled = false; dtpFrom.Enabled = false; dtpTo.Enabled = false; cboType.Enabled = false; txtPath.Enabled = false; btnBrowse.Enabled = false; btnExport.Enabled = false; } ////// State 3: Connection string not null /// private void go_state_3() { _StateVariable = 3; // enable controls. radioButton1.Enabled = true; radioButton1.Checked = true; radioButton2.Enabled = true; cboType.Enabled = true; txtPath.Enabled = true; btnBrowse.Enabled = true; go_state_4(); go_state_7(); go_state_10(); } ////// State 4: Region A: Transient /// private void go_state_4() { _StateVariable_A = 4; if( radioButton2.Checked ) go_state_5(); else if( radioButton1.Checked ) go_state_6(); } ////// State 5: Region A: "Period" checked /// private void go_state_5() { _StateVariable_A = 5; dtpFrom.Enabled = true; dtpTo.Enabled = true; } ////// State 6: Region A: "All" checked /// private void go_state_6() { _StateVariable_A = 6; dtpFrom.Enabled = false; dtpTo.Enabled = false; } ////// State 7: Region B: Transient /// private void go_state_7() { _StateVariable_B = 7; if( txtPath.Text == "" ) go_state_8(); else go_state_9(); } ////// State 8: Region B: "output file" null /// private void go_state_8() { btnExport.Enabled = false; } ////// State 9: Region B: "output file" not null /// private void go_state_9() { btnExport.Enabled = true; } ////// State 10: Region C: "Export type" is XML /// private void go_state_10() { _StateVariable_C = 10; cboType.SelectedIndex = 0; } ////// State 11: Export /// /// private void go_state_11() { } #endregion #region UI independence routines ////// I don't consider this button in State chart. /// And I am lazy to change.... /// /// private bool testConnection( string connectString ) { } private bool export( int type, DateTime dtFrom, DateTime dtTo, string outputFile ) { } #endregion #region Event handler private void btnExport_Click(object sender, System.EventArgs e) { int iType = 0; if( radioButton1.Checked == true ) iType = 1; else if( radioButton2.Checked == true ) iType = 2; if( export( iType, dtpFrom.Value, dtpTo.Value, txtPath.Text ) ) MessageBox.Show( "Finished!" ); else MessageBox.Show( "Fail!!" ); } private void btnBrowse_Click(object sender, System.EventArgs e) { // Displays a SaveFileDialog so the user can save the Image SaveFileDialog saveFileDialog1 = new SaveFileDialog(); saveFileDialog1.Filter = "XML file|*.xml"; saveFileDialog1.Title = "Save to XML File"; saveFileDialog1.ShowDialog(); saveFileDialog1.OverwritePrompt = true; // If the file name is not an empty string open it for saving. if(saveFileDialog1.FileName != "") { txtPath.Text = saveFileDialog1.FileName; } } private void btnTest_Click(object sender, System.EventArgs e) { testConnection( txtConnectString.Text ); } private void radioButton1_CheckedChanged(object sender, System.EventArgs e) { go_state_6(); } private void radioButton2_CheckedChanged(object sender, System.EventArgs e) { go_state_5(); } private void txtPath_TextChanged(object sender, System.EventArgs e) { go_state_7(); } private void txtConnectString_TextChanged(object sender, System.EventArgs e) { go_state_1(); } #endregion }
RFMaintainer 的 statechart
看Constructing the user interface with statecharts之後,應用到自己的小程式上…
書上建議先大致勾勒出畫面,然後寫出 screen rule.
我的畫面:
而 screen rule 大致如下:
*”Connection string” must be specified.
*如果”Connection string” is not specified, “All”, “Period”, “From”, “To”, “Export type”, “output file”, “browse”, “export” 都會被 disable.
*當按下 “Export” button 會開始進行 export 動作.
*當按下 “Browse” button 可以指定 output file
*如果 output file 未指定, “Export” 按鈕無法被 click.
*選擇 Option “All”, Option “Period” will be disabled.
*選擇 Option “Period”, Option “All” will be disabled.
*Export type 目前只有一個可以選擇: XML
所以,可以導出這樣的 statechart,只是不知道對不對…
最後寫 code 的時候,我為每個 state 加上編號,如圖:
然後再寫,的確是比較容易寫 code, 也比較容易了解.
只是,看來並沒有跟 UI 分離,或許是我自己理解錯誤…
升級到 MovableType 2.661
拖拖拖….轉眼就拖了一年,Movable Type都已經到 3.17 版了,而我…還在用 2.64.
原因是因為我以為下載不到 2.661 了….
其實上官方網站免費加入會員以後,還是可以在帳戶裡面下載到 2.661 版.
再配合Jedi的Movable Type完全手冊,一下子就搞定了.
升級上,沒啥問題. MySQL database 不用動,只要先備份 mt.cfg 以及 mt-db-passwd.cgi.
然後把新的檔案複製過去,最後再把剛剛備份的檔案覆蓋回來就好啦~~
收工.
如何判別是否為 DRM 的 ASF 檔案?
如何判別一個 ASF 檔案是否為 DRM 的 ASF 檔案?
詳情資料可以參考微軟提供的 Specification: Advanced Systems Format (ASF) Specification
大致上很簡單,一個 ASF 檔案主要有三個部分: Header Object, Data Object, Index Object.
而每個部分又各由不同的 Object 所組成.
要分辨是否為 DRM,主要是在 Header Object 裡面找尋是否有 Content Encryption Object.
那麼要怎麼找尋呢?? OK, 每個 Object 的最前面有唯一的 GUID,你可以依據 GUID 來找到.
而這些,你都可以在 Specification 的後面幾張找到 ( 或者你可以搜尋 ASF GUIDs ).
至此,寫起來並不太困難.程式碼如下:
typedef unsigned long UINT32;
typedef unsigned short UINT16;
typedef unsigned char UINT8;
typedef int BOOL;
enum BOOLEAN
{
FALSE = 0,
TRUE
};
typedef struct GUID_s
{
UINT32 id1;
UINT16 id2;
UINT16 id3;
UINT16 id4;
UINT8 id5[6];
}GUID;
//Content Encryption Object
GUID ASF_Content_Encryption_Object_guid =
{
0x2211b3fb,
0xbd23,
0x11d2,
0xb7b4,
{
0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e
}
};
/*
typedef struct ContentEncryptionObject_s
{
GUID objectID;
UINT32 objectSize; // objectSize should be QWORD, 8 bytes, but CCS has not this type.
UINT32 objectSize1;
UINT32 secretDataLength;
BYTE* secretData; // according secretDataLength
UINT32 protectionTypeLength;
char* protectionType;
UINT32 keyIDLength;
char* keyID;
UINT32 licenseURLLength;
char* licenseURL;
}ContentEncryptionObject_t;
*/
//extern CF_FILE* filePtrR;
typedef FILE CF_FILE;
CF_FILE* filePtrR;
long
FILEr_ftell()
{
return ftell( filePtrR );
}
int
FILEr_fseek( long filepos, int whence)
{
return fseek( filePtrR, filepos, whence );
}
long
FILEr_fread( UINT8* ptr, long length )
{
return fread( ptr, 1, length, filePtrR );
}
char* SDRAM_HOLE = NULL;
BOOL
isASFDRM( )
{
long filePos;
char* spaceForParsing = (char*)( SDRAM_HOLE + 0x800000 ); // use the space after 8 MB, prevent conflict
int GUIDLen = sizeof( GUID );
int iter, len = 0;
BOOL bFound = FALSE, bResult = FALSE;
UINT32 headerObjectSize = 0;
UINT32 secretDataLength=0;
UINT32 protectionTypeLength=0;
char* protectionType=NULL;
long readLen=0;
char* encryptionPos = NULL;
// save file position
filePos = FILEr_ftell();
// seek to the start position
FILEr_fseek( 16, 0); //SEEK_SET
FILEr_fread( (UINT8*)&headerObjectSize, 4 ); // we should read 8 here, but ….
// read all header object
// then search the specified GUID, use pattern compare
FILEr_fseek( 0, 0 ); // SEEK_SET
readLen = FILEr_fread( spaceForParsing, headerObjectSize );
len = headerObjectSize – GUIDLen;
// searching.
for( iter = 0; iter objectID
encryptionPos = spaceForParsing + iter;
// ( encryptionPos + GUIDLen ) ==> objectSize
// ( encryptionPos + GUIDLen + 8 ) ==> secretDataLength
secretDataLength = (UINT32) *( encryptionPos + GUIDLen + 8 );
// ( spaceForParsing + iter + GUIDLen + 8 + 4 ) ==> secretData
// ( spaceForParsing + iter + GUIDLen + 12 + secretDataLength ) ==> protectionTypeLength;
protectionTypeLength = (UINT32) *(encryptionPos + GUIDLen + 12 + secretDataLength );
protectionType = (char*) (encryptionPos + GUIDLen + 12 + secretDataLength + 4 );
if( strncmp( protectionType, “DRM”, 3 ) == 0 )
bResult = TRUE;
OnFinally:
// restore file position
FILEr_fseek( filePos, 0); //SEEK_SET
// return result.
return bResult;
}
int main(int argc, char* argv[])
{
SDRAM_HOLE = (char*) malloc( 0x800000 * 2 );
filePtrR = fopen( “00 Cannot Play.wma”, “rb” );
if( isASFDRM() )
printf(“is a drm file.\n”);
else
printf(“not a drm file.\n”);
fclose( filePtrR );
return 0;
}
ok, 程式裡面你會發現一些奇怪的地方,是的,因為我需要在 embedded 環境下運作,所以為了符合環境,所以做了一些調整,不過你還是可以在 Visual C++ 上來運行這段代碼.
看不順眼的,就自己調整吧…
這份 specification 其實斷斷續續看一陣子了,都沒怎麼專心看,不過昨天看的時候,突然開竅了,於是就把這麼一段代碼搞定了.
真神奇啊….
跳槽到 CentOS 3.4
Upgrade to MediaWiki 1.4.4
鬼混了很久,終於把家裡的 wiki 升級到 1.4.4 了.
升級方法還挺簡單的.
1. 備份你的 LocalSettings.php / AdminSettings.php
2. 把下載來的 MediaWiki 1.4.4 tarball 解壓以後整個蓋過去.
3. 切到 maintenance 目錄下, 執行 php update.php
4. 收工.
真的很簡單吧….
下個目標是把 VFP Wiki 升級到 1.4.4 …