nighty

          折騰的年華
          posts - 37, comments - 143, trackbacks - 0, articles - 0

          2008年8月21日

          系統(tǒng)為ubuntu server,二臺(tái)機(jī)器A和B,IP為A 192.168.1.111,B 192.168.1.222
          A為rsync server,啟動(dòng)為守護(hù)進(jìn)程,B為備份機(jī),做為rsync client,最后用crontab做一個(gè)簡(jiǎn)單的作業(yè),定時(shí)在B上執(zhí)行同步文件的功能
          A的安裝和配置如下:
          1.  apt-get install rsync   可能提示系統(tǒng)已經(jīng)安裝有了
          2. 配置文件/etc/rsyncd.conf
              默認(rèn)安裝時(shí)是不會(huì)有這個(gè)配置文件的,但是可以 cp /usr/share/doc/rsync/examples/rsyncd.conf /etc  把它示例中的配置文件拷貝過來
              vi /etc/rsyncd.conf    這里參數(shù)有點(diǎn)多,但是有些可以先不管,關(guān)注重點(diǎn)的
              [ftp]  這里是模塊,可以配置多個(gè),這個(gè)是系統(tǒng)默認(rèn)給出的一個(gè)配置,下面給一個(gè)本機(jī)上的配置示例:
          --------------------------------------------------------------------------------------------------
          # so omit the "pid file" line completely in that case.
          pid file=/var/run/rsyncd.pid
          #syslog facility=daemon
          #socket options=
          # MODULE OPTIONS
          [share]
          comment = public archive
          path = /var/www/pub
          use chroot = no
          max connections=2
          # lock file = /var/lock/rsyncd
          # the default for read only is yes...
          read only = no
          list = yes
          uid = nobody
          gid = nogroup
          # exclude = 
          # exclude from = 
          # include =
          # include from =
          auth users = rsync
          secrets file = /etc/rsyncd.secrets
          strict modes = yes
          hosts allow = 192.168.1.222
          # hosts deny =
          ignore errors = yes
          ignore nonreadable = yes
          transfer logging = yes
          log format = %t: host %h (%a) %o %f (%l bytes). Total %b bytes.
          timeout = 600
          refuse options = checksum dry-run
          dont compress = *.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz
          ---------------------------------------------------------------------------------------------
          這里,最上面的是pid文件位置。然后配置了一個(gè)模塊名叫做share,最大連接數(shù)是2,read only = no,指定為非只讀(不然同步時(shí)會(huì)有權(quán)限問題)
          而后面的auth users = rsync 是指定一個(gè)同步的賬戶名叫做rsync,這個(gè)賬戶的認(rèn)證文件是/etc/rsyncd.secrets,當(dāng)然我們要?jiǎng)?chuàng)建這個(gè)文件

          3.  創(chuàng)建 /etc/rsyncd.secrets文件,內(nèi)容為: rsync:123  表示rsync這個(gè)用戶的密碼是123 然后修改文件的權(quán)限 chmod 600 /etc/rsyncd.secrets

          4.  rsync server做為守護(hù)進(jìn)程
               vi /etc/default/rsync
               可以看到開頭處這樣聲明:
          ------------------------------------
          # start rsync in daemon mode from init.d script?
          #  only allowed values are "true", "false", and "inetd"
          #  Use "inetd" if you want to start the rsyncd from inetd,
          #  all this does is prevent the init.d script from printing a message
          #  about not starting rsyncd (you still need to modify inetd's config yourself).
          RSYNC_ENABLE=inetd
          -------------------------------------------
             做為守護(hù)進(jìn)程,可以設(shè)置為true或是xinetd方式來啟動(dòng)。于是我們安裝inetd   sudo apt-get install xinetd
             安裝好后配置inetd的配置文件  vi /etc/xinetd.d/rsync ,輸入如下內(nèi)容:
          ---------------------------------------------------
          service rsync
          {
              disable = no
              socket_type = stream
              wait = no
              user = root
              server = /usr/bin/rsync
              server_args = --daemon
              log_on_failure += USERID
          }
          -------------------------------------------------------
          然后啟動(dòng)xinetd,/etc/init.d/xinetd restart,A服務(wù)器的rsyncd server就完成了!

          5.  B服務(wù)器由于是client,不需要配置,也不需要安裝xinetd,直接可以通過命令行執(zhí)行
          rsync --delete -azvv rsync@192.168.1.111::share /var/www/pub/
          這個(gè)命令就可以直接連接到192.168.111的rsync賬戶,它會(huì)提示你輸入密碼,就是A中的secrets文件中的密碼,然后同步share模塊到本機(jī)的/var/www/pub目錄,你可以事前在A機(jī)器上創(chuàng)建一個(gè)文件如test.txt,隨便寫點(diǎn)內(nèi)容,然后執(zhí)行些命令,看是不是B上多了這樣一個(gè)文件?如果是,則表示已經(jīng)連接成功。你接下來就可以做crontab了!

          posted @ 2013-04-12 12:23 寒武紀(jì) 閱讀(1347) | 評(píng)論 (0)編輯 收藏

          二臺(tái)服務(wù)器,A的內(nèi)網(wǎng)IP為192.168.1.111,B的內(nèi)網(wǎng)IP為192.168.1.222,A做為master,B做為Slave
          1.  配置A的Mysql
               (1)  vim /etc/mysql/my.cnf
                     去掉[mysqld]段中 server_id =1 和log_bin=/var/log/mysql/mysql-bin.log的#注釋
                     加上  binlog-do-db = s3     s3就是要同步的數(shù)據(jù)庫的名稱,如果沒有這一行,表示同步所有的數(shù)據(jù),另外 binlog_ignore_db = mysql。要表示忽略同步的數(shù)據(jù)庫名稱為mysql,如果有多個(gè)要指定同步或是忽略同步的數(shù)據(jù),就配置多行,保存退出。
               (2) 創(chuàng)建一個(gè)復(fù)制用的賬戶(名稱為repl,允許從遠(yuǎn)程連接,密碼為123456):
                    GRANT REPLICATION SLAVE, RELOAD,SUPER, NO *.* TO repl@'%' IDENTIFIED BY '123456';
                  FLUSH PRIVILEGES;
               (3) 重啟mysql服務(wù),或是直接reboot機(jī)器也可以
               (4) 進(jìn)入mysql,然后用 show master status\G  查看二進(jìn)制日志的狀態(tài),看到類似以下的結(jié)果:
                    +------------------+----------+--------------+------------------+
                    | File                      | Position  | Binlog_Do_DB | Binlog_Ignore_DB |
                    +------------------+----------+--------------+------------------+
                    | mysql-bin.000003 |     1376  | s3                  |                           |
                    +------------------+----------+--------------+------------------+
          2.  配置B的Mysql
                (1) vim /etc/mysql/my.cnf
                     去掉[mysqld]段中 server_id =1 和log_bin=/var/log/mysql/mysql-bin.log的#注釋,把server_id改為2,要和master機(jī)器的不一樣。并增加以下內(nèi)容:
                     binlog_do_db=s3
                     log-slave-updates
                    保存退出
               (2) 重啟mysql服務(wù)
               (3) 進(jìn)入mysql,執(zhí)行
                    CHANGE MASTER TO MASTER_HOST='192.168.1.111', MASTER_USER='repl',Master_Port=3306,MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000003',MASTER_LOG_POS=1376;
                    SLAVE START;
                    注意上面的CHANGE語句中,MASTER_LOG_FILE和MASTER_LOG_POS就是上面1.4中提到的show master status命令得到的結(jié)果,指定二進(jìn)制文件的名稱和開始同步的位置。
               (4) 查看SLAVE狀態(tài):    show slave status\G
          *************************** 1. row ***************************
                         Slave_IO_State: Waiting for master to send event
                            Master_Host: 192.168.1.111
                            Master_User: repl
                            Master_Port: 3306
                          Connect_Retry: 60
                        Master_Log_File: mysql-bin.000003
                    Read_Master_Log_Pos: 1376
                         Relay_Log_File: mysqld-relay-bin.000002
                          Relay_Log_Pos: 1355
                  Relay_Master_Log_File: mysql-bin.000003
                       Slave_IO_Running: Yes
                      Slave_SQL_Running: Yes
                        Replicate_Do_DB: 
                    Replicate_Ignore_DB: 
                     Replicate_Do_Table: 
                 Replicate_Ignore_Table: 
                Replicate_Wild_Do_Table: 
            Replicate_Wild_Ignore_Table: 
                             Last_Errno: 0
                             Last_Error: 
                           Skip_Counter: 0
                    Exec_Master_Log_Pos: 1376
                        Relay_Log_Space: 1512
                        Until_Condition: None
                         Until_Log_File: 
                          Until_Log_Pos: 0
                     Master_SSL_Allowed: No
                     Master_SSL_CA_File: 
                     Master_SSL_CA_Path: 
                        Master_SSL_Cert: 
                      Master_SSL_Cipher: 
                         Master_SSL_Key: 
                  Seconds_Behind_Master: 0
          Master_SSL_Verify_Server_Cert: No
                          Last_IO_Errno: 0
                          Last_IO_Error: 
                         Last_SQL_Errno: 0
                         Last_SQL_Error: 
            Replicate_Ignore_Server_Ids: 
                       Master_Server_Id: 1
              上面的紅色二行如果為YES則表示已經(jīng)正常連接,可以進(jìn)行復(fù)制了。

          posted @ 2013-04-09 20:33 寒武紀(jì) 閱讀(1737) | 評(píng)論 (0)編輯 收藏

              許久沒用服務(wù)器上裝的那個(gè)postgresql,其實(shí)是用來做redmine的數(shù)據(jù)庫的,考慮到mysql可能經(jīng)常升級(jí),而rails的連接組件在安裝上有點(diǎn)麻煩,所以當(dāng)初就裝成postgresql。
              今天準(zhǔn)備備份一下,用的phppgadmin,剛開始是提示其中的pg_dump執(zhí)行路徑?jīng)]有配置,重新配置好后,導(dǎo)出的結(jié)果卻是空的損壞文件。于是想算了,還是轉(zhuǎn)到pg的安裝目錄下執(zhí)行pg_dump.
              服務(wù)器裝的是centos 6.2,忘了當(dāng)初是建了一個(gè)用戶postgres.postgres進(jìn)行安裝的,用其它用戶切換到pg的安裝目錄下bin/pg_dump是執(zhí)行不了的,提示在指定目標(biāo)下生成導(dǎo)出文件。故猜測(cè)應(yīng)該是postgres這個(gè)用戶的權(quán)限不足!
              cat /etc/passwd查看一下當(dāng)前有多少用戶,的確有postgres.postgres用戶,密碼多少?忘了!反正有root,直接passwd修改成新的密碼吧,于是就立馬修改了該用戶的密碼,可以正確切換到postgres用戶了,還需要root為postgres指定一個(gè)目錄有操作權(quán)限
              chown -R postgres.postgres /var/xxxx   
              然后再回到pg的bin目錄下,執(zhí)行pg_dump redmine > /var/xxx/redmine.bak
              這下終于正常了,別忘了還得去redmine安裝目錄下,備份下files文件夾。
              
              僅以此為筆記,以后可以查閱使用

          posted @ 2013-03-27 22:39 寒武紀(jì) 閱讀(1402) | 評(píng)論 (0)編輯 收藏

                用Flex做企業(yè)應(yīng)用將近有一年時(shí)間了,這個(gè)過程很累,在國(guó)內(nèi)這方面的積累不多,真正有參考意義的資料的確非常少。經(jīng)過一段長(zhǎng)時(shí)間的摸索后,多少也積累了一點(diǎn)經(jīng)驗(yàn),就最近的關(guān)于單元測(cè)試的想法做一點(diǎn)總結(jié),由于涉及的知識(shí)較多,這里也只是給出個(gè)人的一種思路。
               眾所周知,F(xiàn)lex的缺點(diǎn)是開發(fā)調(diào)試效率較低,而且它只是表現(xiàn)層的一種解決方案。在企業(yè)應(yīng)用中最需要解決的是編譯生成的swf體積問題,我想任何客戶都很難接受一個(gè)企業(yè)應(yīng)用全部打包在一個(gè)swf里,幾MB甚至幾十MB的初始化過程誰都無法接受,所以都必不可少地采用Module的加載方式,把不同的業(yè)務(wù)功能編譯成獨(dú)立的swf,需要用的時(shí)候再去加載。把核心功能、通信機(jī)制、公共組件設(shè)計(jì)成庫項(xiàng)目,編譯成swc做為RSL讓業(yè)務(wù)模塊共享調(diào)用,達(dá)到盡量減少業(yè)務(wù)模塊編譯體積的目的。在這方面如果用心優(yōu)化的話,基本上可以控制到每個(gè)swf體積大概在200KB以內(nèi),這樣就算是互聯(lián)網(wǎng)方式部署,客戶訪問仍是可以接受的。
              

               該結(jié)構(gòu)圖給出了一種整體的設(shè)計(jì)方案,F(xiàn)lex的啟動(dòng)肯定得有Application,這個(gè)是用戶初登錄后第一個(gè)加載的swf(登錄就不要用flex了,用jsp或是模板實(shí)現(xiàn)吧)。所以它負(fù)責(zé)加載你設(shè)計(jì)的整個(gè)框架,包含模塊加載機(jī)制、通信代理方式、基礎(chǔ)庫初始化等等,而和Java端的通信目前比較有效的仍然是blazeds,這個(gè)技術(shù)需要的介紹內(nèi)容不在本文的范圍之內(nèi)。關(guān)于通信接口的實(shí)現(xiàn)有一種非常有用的方式就是借用Java的動(dòng)態(tài)代理理念,Spring有一個(gè)flex的擴(kuò)展子項(xiàng)目叫做springactionscript,而這個(gè)項(xiàng)目又引用了as3commons的庫(類似于apache commons的一些公共組件)。為什么提及這個(gè),因?yàn)閒lex本身的反射功能api非常難用,所以as3commons就做了擴(kuò)展,它大大簡(jiǎn)化了反射的使用,而且提供了一個(gè)bytecode的工具用于操作字節(jié)碼,它是實(shí)現(xiàn)動(dòng)態(tài)代理的關(guān)鍵。至于為什么要?jiǎng)討B(tài)代理?目的就是達(dá)到在寫和Java對(duì)接的接口時(shí),可以只聲明接口,不需要實(shí)現(xiàn)類(得減少多少重復(fù)代碼呀?),而和Java對(duì)接接口我們又可以開發(fā)一個(gè)工具讓java code 自動(dòng)轉(zhuǎn)成 as code,如果懂得Eclipse插件開發(fā)的話還可以進(jìn)一步做一個(gè)插件,達(dá)到Java只寫一次就可以自動(dòng)生成對(duì)應(yīng)的flex接口,提高開發(fā)效率。
               轉(zhuǎn)入正題,關(guān)于單元測(cè)試的概念,F(xiàn)lash Builder在4.5已經(jīng)把flex unit作為內(nèi)置庫了,這點(diǎn)和Eclipse把junit內(nèi)置類似,而flex unit的使用網(wǎng)上有大量的資料介紹,這里也不多說。flex unit在測(cè)試as代碼還是不錯(cuò)的,和junit測(cè)試一樣,提供了一些簡(jiǎn)單的Assert斷言,但是你最痛苦的卻不是as的測(cè)試。企業(yè)開發(fā)的特點(diǎn)就是數(shù)據(jù)量不大,但是需求坑爹,經(jīng)常變來變?nèi)ィ医Y(jié)構(gòu)復(fù)雜,往往一張表很多字段,關(guān)聯(lián)子表,層級(jí)屬性多。而你如果選擇了Flex做了展示層的技術(shù),那必定是看中它比HTML + CSS + JS更強(qiáng)的界面交互功能。的確,這點(diǎn)不容質(zhì)疑,F(xiàn)lex Spark的皮膚機(jī)制的確提供了很多優(yōu)秀的特點(diǎn),不過如果你想純熟掌握它的整個(gè)機(jī)制,恐怕得花很多時(shí)間閱讀源代碼才行,而皮膚的制作整對(duì)別想讓美工獨(dú)自實(shí)現(xiàn),它同樣是需要技術(shù)積累的,介紹它需要用幾個(gè)篇幅才足夠。任何技術(shù)方案都一樣,BS、CS、AIR在實(shí)現(xiàn)復(fù)雜界面時(shí),對(duì)于開發(fā)人員來說,最痛苦的莫過于界面的單元測(cè)試。
               痛苦在哪里?回看上面那幅架構(gòu)圖,業(yè)務(wù)功能界面實(shí)現(xiàn)在Flex,業(yè)務(wù)邏輯在后臺(tái)Java,那么當(dāng)二個(gè)團(tuán)隊(duì)同時(shí)進(jìn)行工作的時(shí)候,溝通就是最大的成本。解決溝通的問題就必須在先前設(shè)計(jì)時(shí)約定好接口和數(shù)據(jù)結(jié)構(gòu),那是會(huì)影響雙方團(tuán)隊(duì)協(xié)調(diào)的關(guān)鍵因素。當(dāng)雙方同時(shí)進(jìn)行開發(fā)的時(shí)候,勢(shì)必存在前臺(tái)依賴后臺(tái)的情況,因?yàn)樗艿竭_(dá)界面的前提得在整個(gè)框架載入后(并且可以初始化一堆數(shù)據(jù),發(fā)生了通信),Java后臺(tái)還好說,依賴于spring和junit可以做到很好的單元測(cè)試。而flex就痛苦了,我沒有通訊啥都做不了呀!
               如何設(shè)計(jì)單元測(cè)試?最大的出發(fā)點(diǎn)就是如何切掉和后臺(tái)通信連接,看下面的簡(jiǎn)單結(jié)構(gòu)圖

               實(shí)現(xiàn)思路介紹:
               1.  簡(jiǎn)化Application加載過程   --   可以套用你主程序中的加載過程,但是不需要你的主界面其它多余的元素,達(dá)到減少到達(dá)測(cè)試界面的多余步驟(盡可能少地減少鼠標(biāo)和鍵盤操作)
               2.  定義測(cè)試配置   --   測(cè)試哪個(gè)模塊?哪個(gè)工作流程?你得通過配置的方式來定義,而不是每次都手寫代碼,才能方便你的成員使用你這個(gè)工具
               3.  模擬后臺(tái)接口實(shí)現(xiàn)  --  記得上面說的動(dòng)態(tài)代理嗎?其實(shí)是為接口動(dòng)態(tài)生成一個(gè)實(shí)現(xiàn)類,然后注入真正通信的實(shí)現(xiàn)代碼,例如WebService、HTTP,既然可以注入這些通信渠道,當(dāng)然就可以注入本地實(shí)現(xiàn)類啦
               4.  對(duì)象查看器    --   這個(gè)是神馬?因?yàn)槟愣疾灰狫ava后臺(tái)了,每次操作一個(gè)界面后得提交數(shù)據(jù)吧?沒有后臺(tái)了,提交到哪里?你得必須把你的提交對(duì)象用界面展示出來吧?好吧,這個(gè)可是個(gè)難點(diǎn)!

               我想這四個(gè)方面的原則無非就是:減少單元測(cè)試需要進(jìn)行的步驟(最快到達(dá)測(cè)試界面),脫離后臺(tái)依賴(自己簡(jiǎn)單模擬后臺(tái)實(shí)現(xiàn),可惜flex沒有類似java的mock庫,悲劇!),如何查看提交到后臺(tái)的結(jié)果。 單元測(cè)試的目標(biāo):界面能正常加載、提交數(shù)據(jù)正常,如果二者都沒問題,那么聯(lián)調(diào)的時(shí)候就可以非常容易定位到是Flex的問題還是Java的問題!達(dá)到介分責(zé)任的目標(biāo),當(dāng)然,如果你所在團(tuán)隊(duì)是按模塊分的,也就是說flex和java都是同一個(gè)人做,那么就不存在責(zé)任問題。

               怎么實(shí)現(xiàn)上面的四個(gè)步驟呢?簡(jiǎn)要地介紹一下吧。
               第1簡(jiǎn)化application加載,其實(shí)你可以把第一張圖中的application加載機(jī)制拷貝過來,只是主界面可以做得非常簡(jiǎn)單,比如不需要多余的控件(比如過長(zhǎng)的菜單、當(dāng)前登錄人、時(shí)間、一陀設(shè)置按鈕等),只留下最核心的能到達(dá)你測(cè)試界面的入口,至于怎么設(shè)計(jì)這個(gè)簡(jiǎn)化版的application,那得發(fā)揮你本人的創(chuàng)造力,另外還得看具體的業(yè)務(wù)。

              第2定義測(cè)試配置。模塊如何加載?通信接口本地模擬實(shí)現(xiàn)類定義?通過配置顯示在appliation做為觸發(fā)控件,這些你都得自定義一套xml之類的文件來配置吧,這個(gè)就需要技巧了,不能設(shè)計(jì)得太復(fù)雜,因?yàn)槟愕拈_發(fā)人員需要沿用你定義的規(guī)范來定義它需要測(cè)試的模塊,關(guān)于這方面的知識(shí),可以參考spring加載配置文件方式、struts2加載定義文件等理念,有一個(gè)概念我比較推薦,就是struts2中的include配置文件,允許配置文件分散,讓大家提供代碼和文件時(shí)減少?zèng)_突,又可以套用你正常的加載機(jī)制。

              第3模擬后臺(tái)接口實(shí)現(xiàn)。這個(gè)是比較煩的,模擬機(jī)制本身通過動(dòng)態(tài)代理倒是不難實(shí)現(xiàn),惡心的是你得自己動(dòng)手用flex簡(jiǎn)單實(shí)現(xiàn)一次后臺(tái)生成數(shù)據(jù)、處理數(shù)據(jù)的邏輯。這里我有個(gè)實(shí)踐的總結(jié)經(jīng)驗(yàn)分享,在前期你調(diào)試完的后臺(tái)接口證明是沒有問題的,那么可以混合使用,一部分調(diào)試過的接口可以直接用后臺(tái),而新接口才本地模擬。一個(gè)原則就是后臺(tái)有的,已經(jīng)證明穩(wěn)定的就用后臺(tái),沒有的或是后臺(tái)還沒有完成的你就自己模擬。

              第4對(duì)象查看器。想想flex不能操作數(shù)據(jù)庫、由于安全限制不允許直接操作文件、無法讀取本地文件目錄。而你的測(cè)試數(shù)據(jù)也許會(huì)有關(guān)聯(lián)(特別是在工作流方面),所以你得想一個(gè)方案來保存你的對(duì)象結(jié)果,而且得以一種人性化的方式查看對(duì)象內(nèi)容。且拋開數(shù)據(jù)存儲(chǔ)的問題,這個(gè)對(duì)象查看器如何設(shè)計(jì)就夠你頭疼的了,首先是對(duì)象得定義成一種格式,一種人可以看得懂的格式,比如xml,可以支持序列化和反序列化,你得去掉多余的無用屬性和訪問器。又得回到反射機(jī)制上了,序列化其實(shí)不難,難的是反序列化時(shí)如何正確地轉(zhuǎn)成原來的對(duì)象。列一種本人設(shè)計(jì)的結(jié)構(gòu):
               <xxx   type="com.xx.oo.XXClass">
                    <aa type="String">aaa</aa>
                    <bb type="Boolean">true</bb>
                    <list type="mx.collection.ArrayCollenction">
                        ....
                    </list>
               </xxx>
               對(duì)象分簡(jiǎn)單對(duì)象、復(fù)雜對(duì)象、動(dòng)態(tài)對(duì)象等,如何表達(dá)這種結(jié)構(gòu)和保證序列化時(shí)不丟失數(shù)據(jù)需要細(xì)心考慮。那么最后如何實(shí)現(xiàn)查看器呢?其實(shí)有一個(gè)參考的范例,就是Eclipse的“大綱”視圖,經(jīng)過實(shí)踐的擴(kuò)展,把樹視圖換成表格樹(這種控件原生沒有,有第三方的可以拿來修改),看個(gè)樣圖吧!
           
               因?yàn)槟汴P(guān)注的對(duì)象內(nèi)容無法就是這三個(gè)方面,屬性名、值、和類型,又支持以樹方式導(dǎo)航對(duì)象,已經(jīng)足夠你人眼分辯內(nèi)容了。至于如何有效的保存測(cè)試數(shù)據(jù),并且組織好結(jié)構(gòu),這個(gè)方面我目前也仍在思考中,未有較好的思路。
               以上內(nèi)容僅是出于本人的一種方案,也許有更好的實(shí)現(xiàn)方案,只是水平不足以超過這種認(rèn)識(shí),希望后續(xù)能進(jìn)一步思考能實(shí)現(xiàn)更加完美的單元測(cè)試框架。
               ST測(cè)試更關(guān)注的界面的自動(dòng)化測(cè)試,這方面涉及的知識(shí)更多,一般公司是很難有財(cái)力和技術(shù)去支持做自動(dòng)化測(cè)試,屬于比較高端的范圍,實(shí)現(xiàn)是很多回歸都靠測(cè)試團(tuán)隊(duì)人肉在實(shí)現(xiàn)。

          posted @ 2012-04-28 12:03 寒武紀(jì) 閱讀(1542) | 評(píng)論 (1)編輯 收藏

                采用Spark進(jìn)行Flex的Web應(yīng)用開發(fā)的時(shí)候,往往會(huì)用一個(gè)固定欄,比如說類似Windows的任務(wù)欄,我們想把它固定在頂部或是底部,用于某些場(chǎng)景存放一些控件。<s:Application>組件中有一個(gè)<s:controlBarContent>屬性和<s:controlBarLayout>,用于設(shè)置Application的一個(gè)組件區(qū)域(我猜官方起的這個(gè)名稱大概用意在于此吧),下面這段話就是官方的中文注釋:
          Title包含在 Application 容器控件欄區(qū)域中的組件集。Application 容器控件欄區(qū)域的位置和外觀由 spark.skins.spark.ApplicationSkin 類確定。默認(rèn)情況下,ApplicationSkin 類定義以灰色背景顯示在 Application 容器內(nèi)容區(qū)域頂部的控件欄區(qū)域。創(chuàng)建自定義外觀以更改控件欄的默認(rèn)外觀。
                于是可以簡(jiǎn)單的給<s:controlBarContent>區(qū)域添加各種控件,以及設(shè)置它內(nèi)在的布局(controlBarLayout)。但是,發(fā)現(xiàn)它只能顯示在Application的頂部!
                這是怎么回事,根據(jù)上面的注釋,感覺應(yīng)該原因在它的皮膚,得跟進(jìn)代碼,看一下究竟。
                首先找到<s:Application>控件的標(biāo)準(zhǔn)Skin,可以在Flash Builder中直接查看。
                
                雙擊打開ApplicationSkin,里面的代碼包含了各種SVG圖形學(xué)的實(shí)現(xiàn)api調(diào)用,flex管這些庫叫FVG,大意就是SVG的Flex實(shí)現(xiàn)版本,該庫實(shí)現(xiàn)得還算簡(jiǎn)潔!
                Application標(biāo)準(zhǔn)皮膚的就是先畫一個(gè)矩形,然后用一個(gè)Group來包含不同的形狀,最后一段<s:Group id="contentGroup" width="100%" height="100%" minWidth="0" minHeight="0" />,代表Application的內(nèi)容區(qū)域,F(xiàn)lex從設(shè)計(jì)上區(qū)分了控件樹和布局樹,有些復(fù)雜,可以參考官方的文檔。因?yàn)锳pplication是屬于容器,所以必須在皮膚中包含這一句,不然程序運(yùn)行時(shí)就看不到它包含的子控件。
               而前面長(zhǎng)長(zhǎng)一串<s:Group id="topGroup">中是畫了頂欄的外觀,它用FVG庫畫了四層:
               <!-- layer 0: control bar highlight -->     底部高亮線(用畫筆填充一個(gè)矩形)
               <!-- layer 1: control bar fill -->              背景填充線性漸變顏色
               <!-- layer 2: control bar divider line -->  分割線
               <!-- layer 3: control bar -->                  controlBar的具體內(nèi)容容器
               當(dāng)然這個(gè)controlBar不是自己會(huì)出現(xiàn)的,只有當(dāng)你填充了內(nèi)容控件的時(shí)候它才顯示,所以有includeIn="normalWithControlBar, disabledWithControlBar",表示在這二個(gè)State下才顯示,什么時(shí)候State才包含這二個(gè)呢?當(dāng)然,就得去看Application.as的實(shí)現(xiàn)原理,具體篇幅就不描述。
               那么回到最初的問題:我想改變controlBar的位置在下方怎么處理?
               從上面的分析可知其實(shí)controlBar的擺放位置是在Skin中定義的,而它是什么布局,它顯示不顯示是通過Application.as本身的代碼控制的,那么我們就只要自定義Application.as的皮膚就可以,新建一個(gè)外觀mxml,直接復(fù)制官方的ApplicationSkin.mxml的代碼,然后,把<s:Group id="contentGroup" width="100%" height="100%" minWidth="0" minHeight="0" />代碼移到<s:Group id="topGroup">代碼的上面,在你的Application中強(qiáng)制指定skinClass為你自定義的ApplicationSkin,或是通過css設(shè)定即可。
               再擴(kuò)展思維一下,其實(shí)我們完全可以把controlBar放到左邊或是右邊,甚至任何位置,都取決你在Skin的定義和Application.as的邏輯控制(你可以繼承Application擴(kuò)展)。
               那么controlBar有什么作用?其實(shí)spark的Panel從及它的子類TitleWindow都有controlBar的概念,它能獨(dú)立于你容器外的區(qū)域,對(duì)于你容器本身包含的組件和布局不會(huì)產(chǎn)生干擾,以及你設(shè)置了width、height為100%時(shí)也不產(chǎn)生影響。如果你不要controlBar,直接在Application中用布局添加一個(gè)Group也是可以實(shí)現(xiàn),但是它從根本上是屬于布局樹中contentGroup的內(nèi)容,會(huì)受限于布局。
               Spark Skin的設(shè)計(jì)的確有高明的地方,對(duì)比Flex3的外觀,它提供給設(shè)計(jì)人員的空間實(shí)在大得多,你可以組合圖片和FVG庫的功能自定義各種外觀,當(dāng)然,我就建議你多熟悉一下FVG庫的應(yīng)用,畢竟從外加載圖片對(duì)flex來說是一個(gè)消耗,你的程序也會(huì)增肥。

          posted @ 2011-10-13 18:14 寒武紀(jì) 閱讀(2482) | 評(píng)論 (0)編輯 收藏

                問題描述:
                    服務(wù)器操作系統(tǒng)是Centos5.5,此前已經(jīng)有多套系統(tǒng)跑在上面,且裝有PHP5.2.10。Centos5.X系統(tǒng)的穩(wěn)定yum安裝源版本是5.1.6,并不符合最新版本的phpmyadmin(5.2以上版本),下載了最新版本phpmyadmin安裝后提示缺少mysql支持模塊。
          查看了一下發(fā)現(xiàn)的確是安裝php的時(shí)候沒有裝上php-mysql模塊。直接重新編譯php源碼安裝比較麻煩。
               一個(gè)比較方便的方法:
              為yum添加第三方的源,然后直接用yum -y install php-mysql進(jìn)行安裝
              以下方法為從網(wǎng)絡(luò)搜索到的,做個(gè)記錄,方面須用之時(shí)查閱。
                    導(dǎo)入地址:    rpm --import http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka
                    編輯yum源: vi  /etc/yum.repos.d/CentOS-Base.repo
                    在最下面添加如下信息:
                             [utterramblings]
                             name=Jason's Utter Ramblings Repo
                             baseurl=http://www.jasonlitka.com/media/EL$releasever/$basearch/
                             enabled=1
                             gpgcheck=1
                             gpgkey=http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka

                
              然后執(zhí)行安裝命令: yum -y install php-mysql    系統(tǒng)就會(huì)自動(dòng)從上面添加的源中讀取合適該版本的php-mysl模塊,安裝完成后重啟httpd服務(wù),再訪問就OK了。
              該方法同樣適合于默認(rèn)安裝的php5.1.6版本安裝成功后再進(jìn)行升級(jí)到 5.2版本。

          posted @ 2011-08-14 15:53 寒武紀(jì) 閱讀(2101) | 評(píng)論 (2)編輯 收藏

              freemarker幾天前才發(fā)布了2.3.17版本,5月21號(hào)又發(fā)布了2.3.18,距2.3.16已經(jīng)一年多了。老的編輯器已經(jīng)不能安裝在新版本的eclipse和myeclipse上面了,最新的官方編輯器仍在開發(fā)階段,有網(wǎng)友貌似知道最新的源代碼鏈接位置,不過肯定是不穩(wěn)定的。
              另一個(gè)可選的編輯器就是JBoss Tools 3.2中的FreeMarker編輯器,值得安慰,啟動(dòng)MyEclipse9.0后進(jìn)入MyEclipse Configuration Center --> Software --> add site,輸入Name: freemarker,    
              URL:   http://download.jboss.org/jbosstools/updates/stable/helios/  然后在All JBoss Tools 3.2.0下選擇FreeMarker IDE,其它的大概你不需要都不用管,也不影響下載時(shí)間,然后一路確認(rèn)安裝就OK!

          posted @ 2011-05-22 22:15 寒武紀(jì) 閱讀(2840) | 評(píng)論 (0)編輯 收藏

                 上周服務(wù)器的一個(gè)PHP軟件不能訪問,查看原因是CentOS的PCRE模塊未用utf-8編碼引起的,由是搜索了一些資料照著變更,沒有效果。
                 當(dāng)時(shí)和另一個(gè)朋友L共同嘗試刪除后重裝,由于對(duì)Linux系統(tǒng)不熟悉,只會(huì)使用常規(guī)的命令進(jìn)行一些皮毛的操作,就直接用yum remove進(jìn)行刪除,系統(tǒng)當(dāng)時(shí)還提示是否remove掉相關(guān)聯(lián)的700多個(gè)組件或模塊。當(dāng)時(shí)也沒有多想就直接回車!結(jié)果------悲劇了,屏幕狂刷,我意識(shí)到pcre是基礎(chǔ)模塊,所有關(guān)聯(lián)它的或是它關(guān)聯(lián)都刪除掉,系統(tǒng)將遭遇一個(gè)災(zāi)難式的破壞。等刷完屏幕的字符,最終發(fā)現(xiàn):所有的bin目錄下的命令全部不見了!!!天哪,連ls命令都沒有,唯獨(dú)剩下一個(gè)cd命令。幸運(yùn)的是,當(dāng)時(shí)運(yùn)行在服務(wù)器的幾個(gè)應(yīng)用還能訪問,比如說phpmyadmin。

                  而后想了想,嘗試了各種方式去恢復(fù),都沒有辦法,那么,唯一的辦法就是盡量備份原有的數(shù)據(jù)和文件,重裝系統(tǒng)進(jìn)行環(huán)境的重新搭建。還好有其它方式,可以先把里面重要的文件都提取了出來。然后在幸存的phpmyadmin上趕緊進(jìn)行mysql相關(guān)數(shù)據(jù)庫的備份。(注:因?yàn)槭莻€(gè)人的服務(wù)器,所以并沒有像公司一樣做好運(yùn)營(yíng)和備份計(jì)劃)

                  第二天決定重裝系統(tǒng)了,管理員用了3個(gè)小時(shí)才搭建起CentOS5.5和SSH遠(yuǎn)程服務(wù)端。輪到我和L需要用SSH進(jìn)行遠(yuǎn)程的環(huán)境搭建。接下來的三天晚上,真是折騰又折騰。計(jì)劃安裝的幾個(gè)主要軟件是:Mysql、PHP、Apache、JDK、Tomcat、Ruby on Rails、Redmine、PHPMyAdmin等。

                  第一個(gè)晚上,災(zāi)難之前裝在上面的上述各種軟件版本都有點(diǎn)低,所以想干脆直接上最新的,而CentOS5.5的yum庫是取不到這些最新的軟件的。所以朋友 L 大量地采用了make install和rpm方式來安裝,而CentOS本身集成的就只有Apache的版本滿足。裝了MySQL5.5+phpMyAdmin3.4,運(yùn)行起來了卻發(fā)現(xiàn)和MySQL5.5和redmine1.1.1有沖突,主要是RoR環(huán)境的Mysql驅(qū)動(dòng)有問題,在網(wǎng)上能找到的都是從http://www.tmtm.org/en/ruby/mysql/ 下載的0.2.6版本,這個(gè)版本我只試在MySQL5.0情況下正常連接。現(xiàn)在換成5.5,就無法運(yùn)行了,為此我還特地把Ruby環(huán)境從1.8.6提高到1.8.7,同樣裝了redmine官網(wǎng)要求的各種Ruby工具和Rails組件,折騰無果!最后,我還把異常信息拿出來,給ruby-mysql的日本作者發(fā)了一個(gè)email,第二天作者回復(fù)我,原來ruby-mysql已經(jīng)掛到GitHub去了,而且現(xiàn)在已經(jīng)是3.0alpha狀態(tài)了,但是他不確定能否工作在ruby1.8.6下。 最后感覺還是不行,切換回MySQL5.0,同樣高版本的ruby-mysql驅(qū)動(dòng)也無法連接低版本的MySQL。第一天以失敗告終!

                 第二天晚上繼續(xù)折騰,再不停地重新安裝,這時(shí)才想起一個(gè)問題:應(yīng)該退回到系統(tǒng)宕機(jī)前的所有軟件版本狀態(tài),這樣原先備份的數(shù)據(jù)才能正常恢復(fù),不會(huì)帶來額外的版本沖突麻煩。于是折騰到接近晚上12:00時(shí)發(fā)覺方向錯(cuò)了,無奈,和 L 打了個(gè)招呼,計(jì)劃明天讓管理員再重裝一次系統(tǒng),現(xiàn)在目前的系統(tǒng)又被我們搞亂了!

                 第三天。就著原來的思路,重裝恢復(fù)到以前的版本,又用yum的原來方式裝回原來的版本,這次進(jìn)行順利。用了二個(gè)小時(shí),就把常用的軟件恢復(fù)了,同時(shí)恢復(fù)了數(shù)據(jù)庫。最后只剩下一個(gè)問題,redmine的密鑰恢復(fù)后,仍然無法連接原來的用戶密碼,我想可能得去查閱redmine的用戶管理模塊,看看它究竟是怎么生成密碼和檢驗(yàn)登錄的,有趣的事是發(fā)現(xiàn)網(wǎng)友找到另一個(gè)方法,就是直接在redmine下建ruby腳本,調(diào)用ActiveReord的User.save(),存一個(gè)自己的新密碼,不曉得可不可行,找個(gè)時(shí)間做個(gè)實(shí)驗(yàn)看看。

                回頭一想。其實(shí)規(guī)劃、整理、理清服務(wù)器的管理工作,比精通Linux系統(tǒng)、各種軟解決技術(shù)更為重要!

          posted @ 2011-05-20 09:24 寒武紀(jì) 閱讀(7801) | 評(píng)論 (2)編輯 收藏

              Phusion Passenger模塊使得Rails應(yīng)用可以像PHP模塊一樣運(yùn)行在Apache上,非常方便。
             準(zhǔn)備條件:CentOS服務(wù)器已經(jīng)裝好了Apache2.2和Redmine應(yīng)用        

              1. 安裝Passenger模塊
                      gem install passenger
                      passenger-install-apache2-module
                  
                      第二個(gè)命令是安裝passenger的apache2模塊,它已經(jīng)做得很智能,會(huì)提示你確認(rèn)安裝以及最后怎么配置模塊到apache中。
                      摘出配置段的內(nèi)容如下:
                          Please edit your Apache configuration file, and add these lines:

                          LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-3.0.7/ext/apache2/mod_passenger.so
                          PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-3.0.7
                          PassengerRuby /usr/bin/ruby

                      把紅色字體部分拷貝到apache的配置文件,可以是主配置文件/etc/httpd/conf/httpd.conf,也可以是在/etc/httpd/conf.d/目錄下新建一個(gè)子文件命名為ruby.conf,推薦第二種方式,更為簡(jiǎn)潔,不會(huì)影響主文件的配置。
                
              2. 先測(cè)試一下passenger是否安裝正常

                      切換到redmine的安裝目錄下,passenger start  命令嘗試一下是否正常能以paasenger方式啟動(dòng),如果沒有異常,恭喜,已經(jīng)安裝完成,剩下的工作就是配置一個(gè)虛擬主機(jī)和子給你的redmine,這樣可以轉(zhuǎn)到更為常用的80端口上。

              3. 配置rails應(yīng)用做為sub URI模式
                       在passenger的官方文檔中其實(shí)有好幾種配置的方式,可以是域名、域名子URI等,很多時(shí)候你可能只有一個(gè)域名,那么利用sub URI來掛不同的應(yīng)用就顯得比較
                      首先配置一段虛擬主機(jī)如下:

                  <VirtualHost *:80>
                      ServerName www.phusion.nl
                      DocumentRoot /websites/phusion
                      <Directory /websites/phusion>
                  Allow from all
                      </Directory>
                  </VirtualHost>

                     解釋一下過程大致是先創(chuàng)建一個(gè)硬鏈接,如下:

                  ln -s /webapps/mycook/public /websites/phusion/rails 
           
               /webapps/mycook/public是你的rails的應(yīng)用目錄下面的public目錄,例如你的redmine安裝在/var/www/html,這個(gè)目錄就是
          /var/www/html/redmine/public,后面就是你Apache主目錄下的創(chuàng)建的一個(gè)鏈接地址rails,意思就是把/var/www/html/redmine/public
          鏈接到/var/www/html/rails,而/var/www/html/rails是實(shí)際上不存在的。
          然后再配置子目錄如下:

          <VirtualHost *:80> ServerName www.phusion.nl DocumentRoot /websites/phusion <Directory /websites/phusion> Allow from all </Directory>
          RailsBaseURI /rails # <-- These lines have <Directory /websites/phusion/rails> # <-- been added. Options -MultiViews # <-- </Directory> # <-- </VirtualHost>

           最后四句帶#注釋說明是重點(diǎn),應(yīng)用RailsBaseURI命令把rails子URI指定到rails的應(yīng)用目錄,而rails目錄就是我們上面鏈接的目錄,而實(shí)際上會(huì)跳到我們的直接redmine目錄。

           

          posted @ 2011-05-19 15:10 寒武紀(jì) 閱讀(2549) | 評(píng)論 (1)編輯 收藏

                  引言:最近又用到dbutils,之前一直用Map映射的方式取出select的結(jié)果再手工做轉(zhuǎn)換。有寫過一篇文章說MapHandler方式的一個(gè)缺陷:關(guān)于commons dbutils組件的一個(gè)小缺陷分析 ,用這種方式,在項(xiàng)目不大的情況下,寫一些Map到JavaBean的轉(zhuǎn)換代碼工作量不大,但是在數(shù)據(jù)庫表過多并且表中的字段過多的情況下,這種重復(fù)的setter感覺有點(diǎn)煩。于是又重新思考了BeanHandler和BeanListHandler的情況,dbutils底層映射用的反射,性能上肯定有損失,不過在大多數(shù)項(xiàng)目規(guī)模不是很大的情況下,這點(diǎn)損失可以忽略,帶來的代碼減少卻是比較可觀。
                  問題在哪里?先看一段官方的示例代碼:

          QueryRunner run = new QueryRunner(dataSource);

          // Use the BeanHandler implementation to convert the first
          // ResultSet row into a Person JavaBean.
          ResultSetHandler<Person> h = new BeanHandler<Person>(Person.class);

          // Execute the SQL statement with one replacement parameter and
          // return the results in a new Person object generated by the BeanHandler.
          Person p = run.query(
              
          "SELECT * FROM Person WHERE name=?", h, "John Doe");

                  這里有個(gè)地方有約束,就是要求示例中的JavaBean類Person中的字段定義要和數(shù)據(jù)庫的字段定義一致。Java的命名習(xí)慣一般是駱峰寫法,例如userId,那么數(shù)據(jù)庫中就必須定義為userId,而問題在于:有時(shí)候我們需要數(shù)據(jù)庫中字段的定義格式與JavaBean的命名不一樣,比如數(shù)據(jù)庫定義為:user_id,而JavaBean則定義為userId
                  看源代碼可能有點(diǎn)費(fèi)時(shí)間,在官方的example頁面的最下面果然有一段關(guān)于自定義BeanProcessor的指引。摘錄出來:

                BasicRowProcessor uses a BeanProcessor to convert ResultSet columns into JavaBean properties. You can subclass and override processing steps to handle datatype mapping specific to your application. The provided implementation delegates datatype conversion to the JDBC driver.
                BeanProcessor maps columns to bean properties as documented in the BeanProcessor.toBean() javadoc. Column names must match the bean's property names case insensitively. For example, the firstname column would be stored in the bean by calling its setFirstName() method. However, many database column names include characters that either can't be used or are not typically used in Java method names. You can do one of the following to map these columns to bean properties:
                1. Alias the column names in the SQL so they match the Java names: select social_sec# as socialSecurityNumber from person
                2. Subclass BeanProcessor and override the mapColumnsToProperties() method to strip out the offending characters.


                大概意思就是提供二種方式:一種就是最直接的,用as關(guān)鍵字把colName重命名,另一種方式就是繼承BeanProcessor類,重寫mapColumnsToProperties()方法。
                那當(dāng)然是第二種方式更加具有代表性。嘗試了一下。代碼如下:
              
           1public class CustomBeanProcessor extends BeanProcessor {
           2    
           3    @Override
           4    protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
           5            PropertyDescriptor[] props) throws SQLException {
           6        int cols = rsmd.getColumnCount();
           7        int columnToProperty[] = new int[cols + 1];
           8        Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);
           9
          10        for (int col = 1; col <= cols; col++{
          11            String columnName = rsmd.getColumnLabel(col); 
          12            if (null == columnName || 0 == columnName.length()) {
          13              columnName = rsmd.getColumnName(col);
          14            }

          15            columnName = colNameConvent(columnName); // 在這里進(jìn)行數(shù)據(jù)庫表columnName的特殊處理
          16            for (int i = 0; i < props.length; i++{
          17
          18                if (columnName.equalsIgnoreCase(props[i].getName())) {
          19                    columnToProperty[col] = i;
          20                    break;
          21                }

          22            }

          23        }

          24        return columnToProperty;
          25    }

          26
          27    /**
          28     * 數(shù)據(jù)庫列名重新約定
          29     * @param columnName
          30     * @return
          31     */

          32    private String colNameConvent(String columnName) {
          33        String[] strs = columnName.split("_");
          34        String conventName = "";
          35        for (int i = 0; i < strs.length; i++{
          36            conventName += StringUtils.capitalize(strs[i]);
          37        }

          38        StringUtils.uncapitalize(conventName);
          39        return conventName;
          40    }

          41}

                  注意mapColumnsToProperties方法的邏輯是從父類的方法中直接復(fù)制出來的,然后在第15行那里變了個(gè)戲法,這里的columnName就是從數(shù)據(jù)庫中讀出來的,自定義一個(gè)private方法用于轉(zhuǎn)換命名,這里你就可以添加自己的命名約束。例如上面就是把 user_id 轉(zhuǎn)化為Java的駱峰寫法:userId
                 再深入一層思考,你可以在這里進(jìn)行更多擴(kuò)展,以便讓自己可以選擇不同的命名轉(zhuǎn)換方式。定義了這個(gè)Processor之后,下面看看如何調(diào)用:
          Connection conn = ConnectionManager.getInstance().getConnection();
          QueryRunner qr 
          = new QueryRunner();
          CustomBeanProcessor convert 
          = new CustomBeanProcessor();
          RowProcessor rp 
          = new BasicRowProcessor(convert);
          BeanHandler
          <User> bh = new BeanHandler<User>(User.class, rp);
          User u 
          = qr.query(conn, sql, bh, params);
          DbUtils.close(conn);
                是不是非常靈活?如果是想返回List結(jié)果的,就把BeanHandler替換成BeanListHander類,還可以再進(jìn)一步封裝這些操作,抽象到公共模塊中去,讓外部直接傳入sql語句和Class就能直接返回想要的結(jié)果,當(dāng)然你得增加泛型的定義。同樣舉一個(gè)封裝的例子:
           1protected <T> List<T> selectBeanList(Connection conn, String sql, Class<T> type,
           2            Object[] params) throws Exception {
           3        log.debug("select sql:[" + sql + "]");
           4        QueryRunner qr = new QueryRunner();
           5        CustomBeanProcessor convert = new CustomBeanProcessor();
           6        RowProcessor rp = new BasicRowProcessor(convert);
           7        ResultSetHandler<List<T>> bh = new BeanListHandler<T>(type, rp);
           8        List<T> list = qr.query(conn, sql, bh, params);
           9        return list;
          10    }

                  至于為什么擴(kuò)展這個(gè)方法就可以實(shí)現(xiàn)這個(gè)邏輯就得去跟源代碼看它的內(nèi)部實(shí)現(xiàn),用了一些JavaBean的處理和反映的技巧來做的。具體就不說。
                  總結(jié):commons組件都設(shè)計(jì)得非常好,可擴(kuò)展性和實(shí)用性都非常高。雖然上面舉例實(shí)現(xiàn)了轉(zhuǎn)換邏輯的替換,但是仍然需要開發(fā)人員在設(shè)計(jì)數(shù)據(jù)庫的時(shí)候和寫JavaBean時(shí)都要嚴(yán)格做好規(guī)范,避免產(chǎn)生不必要的問題。這方面Ruby On Rails就直接內(nèi)部實(shí)現(xiàn),動(dòng)態(tài)語言的優(yōu)點(diǎn)特別能體現(xiàn),同時(shí)強(qiáng)制你在設(shè)計(jì)時(shí)必須用這種方式,典型的約定優(yōu)于配置原則。當(dāng)然,在dbutils里你愿意二種字段名都一樣也無可厚非。
                 缺點(diǎn):BeanProcessor是不支持關(guān)聯(lián)查詢的,所以上面的方式也只能局限于單表的轉(zhuǎn)換,這點(diǎn)就不如myBatis和Hibernate,當(dāng)然用這二個(gè)就引入了一些復(fù)雜性,如何權(quán)衡需要自己衡量,哪個(gè)用得好都一樣。本人就不喜歡myBatis那種把SQL寫到XML中的方式,見過太復(fù)雜的SQL最終在XML里面變得面目全非,如果是接手別人的代碼,是很痛苦的,而且你無法避免只修改XML而不改Java,既然二者都要改,那直接寫Java里又有什么區(qū)別?簡(jiǎn)單就是美。格式和注釋寫好一點(diǎn)同樣很容易理解!

          posted @ 2011-04-26 16:41 寒武紀(jì) 閱讀(4269) | 評(píng)論 (0)編輯 收藏

          posted @ 2011-03-09 16:48 寒武紀(jì) 閱讀(13773) | 評(píng)論 (4)編輯 收藏

          1.  Redmine安裝前提條件

          官方的安裝指南:http://www.redmine.org/projects/redmine/wiki/RedmineInstall

          Notes:

          Ruby 1.9 is not supported yet. You have to use Ruby 1.8.x as stated above.
          RubyGems 1.3.1 or higher is required (Rails 2.3.5 will fail with RubyGems 1.5.0 and later, stick to previous versions of RubyGems)
          Rake 0.8.3 or higher is required
          Rack 1.0.1 is required. If you don't have this exact version, database migration would fail.
          I18n 0.4.2 is required for Redmine >= 1.0.5

           安裝主要版本選擇:ruby 1.8.6rubygems1.3.5rake 0.8.3rack 1.0.1I18n 0.4.2rails 2.3.5

               
          2.  yum安裝ruby

          使用yum安裝ruby相關(guān)的軟件

          yum -y install ruby ruby-devel ruby-libs ruby-irb ruby-rdoc ruby-mysql

          如果沒有ruby-mysql則從http://www.tmtm.org/en/ruby/mysql/ 下載手動(dòng)安裝

          3.
          升級(jí)ruby
          1.8.6版本

          /etc/yum.repos.d/ 目錄下創(chuàng)建yum源文件ruby.repo,內(nèi)容如下:

          --------------------------------------------------------------------------------

          [ruby] 
          name=ruby 
          baseurl=http://repo.premiumhelp.eu/ruby/ 
          gpgcheck=0 
          enabled=0 

          --------------------------------------------------------------------------------

          升級(jí)ruby

          yum --enablerepo=ruby update ruby

          4.  安裝rubygems1.3.5

          因?yàn)橹苯油ㄟ^yum安裝的rubygems0.9.4,所以選擇手工下載安裝的方式

          wget http://rubyforge.org/frs/download.php/60718/rubygems-1.3.5.tgz
          tar xzvf rubygems-1.3.5.tgz
          cd rubygems-1.3.5
          ruby setup.rb

          安裝完后運(yùn)行gem –v檢查一下版本是否正常,(當(dāng)然還可以直接用yum安裝,然后通過gem本身的更新來實(shí)現(xiàn),那從rubyforge下載的就應(yīng)該是.gem結(jié)束的升級(jí)文件)

          5.  安裝rails 2.3.5

          gem install rails –v=2.3.5

          6.  安裝Rack 1.0.1

          gem install rack –v=1.0.1

          7.  安裝Rake 0.8.3

          gem install rake –v=0.8.3

          8. 安裝I18n 0.4.2

          gem install -v=0.4.2 i18n

          9.  下載和安裝redmine1.1.1

          wget http://rubyforge.org/frs/download.php/74128/redmine-1.1.1.tar.gz

          拷貝壓縮文件到要安裝的目錄,比如 /var/www目錄下,解壓

          tar xzvf redmine-1.1.1.tar.gz

          cd redmine-1.1.1

          配置數(shù)據(jù)庫連接yml文件(redmine目錄下進(jìn)行如下操作)

          cd config

          cp database.yml.example database.yml

          vi database.yml

          添加如下內(nèi)容:

          production:
          adapter: mysql
          database: redmine
          host: localhost
          username: root
          password: xxx
          socket: /var/lib/mysql/mysql.sock

          數(shù)據(jù)庫要預(yù)先創(chuàng)建好,如果你裝好了mysql,直接運(yùn)行mysql -uroot -p 登錄,然后create database redmine,主機(jī)名、用戶名、密碼也要寫對(duì)。

          10.   生成會(huì)話密鑰

           rake config/initializers/session_store.rb

          11.  rails數(shù)據(jù)庫生成和數(shù)據(jù)初始化

          rake db:migrate RAILS_ENV=production
          rake redmine:load_default_data RAILS_ENV=production

          12.  運(yùn)行測(cè)試

          如果沒有異常,在redmine安裝目錄下執(zhí)行啟動(dòng)服務(wù)器的命令:

          ruby script/server -e production &

          這樣redmine就會(huì)偵聽本機(jī)IP3000端口,輸入URLhttp://IP:3000 就可以看到登陸界面,如果是本機(jī)就直接 http://localhost:3000

          但這樣只是以獨(dú)立的方式啟動(dòng)redmine的服務(wù)器,在后臺(tái)執(zhí)行,有些不足,因?yàn)榭蛻舳说脑L問日志會(huì)在終端上直接顯示。并且你退出終端時(shí),服務(wù)器進(jìn)程也會(huì)跟著關(guān)閉,后面再介紹啟動(dòng)和關(guān)閉腳本的編寫,以及如何用nginx做反向代理,或是用Apache也可以,這個(gè)網(wǎng)上可以搜索到很多資料。

          13.  附注:redmine默認(rèn)端口是3000,如果你是遠(yuǎn)程操作,直接訪問主機(jī)的IP或是域名是無法打開主頁的,因?yàn)?/span>centosiptables默認(rèn)是沒有開通3000端口的,所以需要開放端口。

          打開iptablesvi /etc/sysconfig/iptables

          添加下面一行到文件里面

          -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 3000 -j ACCEPT

          然后重啟iptables

          /sbin/service iptables restart

          到此為止就完成redmine的安裝,過程比較繁瑣,主要是各種組件和模塊的版本匹配問題。大多數(shù)情況下根據(jù)安裝的錯(cuò)誤提示和Google就可以解決滴。

           

          posted @ 2011-03-01 09:47 寒武紀(jì) 閱讀(3099) | 評(píng)論 (1)編輯 收藏

                  非常喜歡這種輕量級(jí)的JDBC封裝,比起Hibernate和iBatis,可以非常自由和靈活地運(yùn)用和自行二次封裝,由于dbutils的BeanHandler轉(zhuǎn)換方式采取了反射技術(shù),在性能上肯定有所損失,所以項(xiàng)目中基本上都使用MapHandler方式來轉(zhuǎn)換數(shù)據(jù),當(dāng)然就是自己寫的代碼多一點(diǎn),也無所謂。一般的查詢、子查詢、聯(lián)合查詢、包括視圖查詢等等都很正常,但是發(fā)現(xiàn)一個(gè)比較小的問題,就是在使用聚合函數(shù)的場(chǎng)所,例如:select user_type, count(*) as count from `user` group by user_type這種類型查詢的時(shí)候,MapHandler方式不起作用,as列都變成key為空串的K-V對(duì),導(dǎo)致有許多地方使用map.get("")代碼的情況出現(xiàn),這種寫法當(dāng)然是不太好的,容易出問題。
                  鑒于前面沒有時(shí)間了解,就都粗略使用了上面那種粗暴的map.get("")來處理,最好的情況是讓dbutils組件能自動(dòng)識(shí)別到as類型的列名。于是有空了就專門看了看它的源代碼,發(fā)現(xiàn)最主要的一段代碼如下:
           1public Map<String, Object> toMap(ResultSet rs) throws SQLException {
           2        Map<String, Object> result = new CaseInsensitiveHashMap();
           3        ResultSetMetaData rsmd = rs.getMetaData();
           4        int cols = rsmd.getColumnCount();
           5
           6        for (int i = 1; i <= cols; i++{
           7            result.put(rsmd.getColumnName(i), rs.getObject(i));
           8        }

           9
          10        return result;
          11    }
                  CaseInsensitiveHashMap是dbutils自定義的一個(gè)Map,忽略鍵大小寫的K-V字典,但是key使用的是ResultSetMetaData.getColumnName(),我想問題大概出在這里,于是認(rèn)真翻了翻java的api文檔(開發(fā)做久了容易遺忘基礎(chǔ)),果然,原來getColumnName()是:獲取指定列的名稱;而as關(guān)鍵字之后,使列名稱變成用于顯示的意義,這個(gè)時(shí)候應(yīng)該使用getColumnLabel():獲取用于打印輸出和顯示的指定列的建議標(biāo)題。建議標(biāo)題通常由 SQL AS 子句來指定。如果未指定 SQL AS,則從 getColumnLabel 返回的值將和 getColumnName 方法返回的值相同。自己手動(dòng)試驗(yàn)了一下,果然如所料,問題就出在這里。
                  所以呢,如果想要dbutils在自動(dòng)轉(zhuǎn)換Map及MapList時(shí)能識(shí)別聚合函數(shù)的列名,那么最好的做法就是重載這種方式,懶一點(diǎn)的,你就干脆修改上面那段代碼,讓它判斷是否使用了as關(guān)鍵字。個(gè)人暫時(shí)搞不清楚官方為什么沒有考慮這一步,有時(shí)間再思考一下!

          posted @ 2011-02-12 17:33 寒武紀(jì) 閱讀(3031) | 評(píng)論 (7)編輯 收藏

              在項(xiàng)目中剛好有一個(gè)地方需要在服務(wù)器端處理一個(gè)請(qǐng)求后,重定向到另一個(gè)Action,這樣瀏覽器的url才會(huì)變成另一個(gè)url,用戶重新刷新時(shí),才不會(huì)彈出一個(gè)對(duì)話框問你是不是要重新提交form。于是就自然而然地用了redirectAction。大概如下:
             
          1<result name="myInfoSuccess" type="redirectAction">
          2    myapp_myInfo.action?msg=${msg}
          3</result>

              因?yàn)橹囟x會(huì)丟失所有的請(qǐng)求參數(shù)和值棧,所以這里轉(zhuǎn)向時(shí),加了一個(gè)請(qǐng)求參數(shù)msg,msg在要重定向的action中設(shè)置。
              問題來了,重定向到myapp_myInfo.action時(shí),這個(gè)Action里面取出msg參數(shù)時(shí)變成亂碼!!! 不論中文或是英語還是數(shù)字,全是亂碼,折騰了一翻,URLEncoderURLDEncoder進(jìn)行URL Base64編碼和解碼處理,包括new String(msg.getBtye("ISO-8859-1"), "UTF-8")這種處理方式仍無法奏效。google了一下并且抱起書本認(rèn)真看了看struts2重定向問題后。大概有了個(gè)思路。
              所有的重定向操作都會(huì)丟失所有的請(qǐng)求參數(shù)、請(qǐng)求屬性等,當(dāng)然包括Action的處理結(jié)果也會(huì)丟失。 
              首先搞清楚redirect、redirectAction的區(qū)別:
              1. redirect類型struts2是調(diào)用HttpServletResponse的sendRedirect(String)方法來重定向到指定的資源,可以是一個(gè)視圖結(jié)果,也可以是其它類型的Action;
              2. redirectAction同樣是重新生成一個(gè)全新的請(qǐng)求。但是struts2內(nèi)部卻是使用ActionMapperFactory提供的ActionMapper來重定向,它只能跳轉(zhuǎn)到另外一個(gè)Action;

                   由于redirectAction使用的是ActionMapper來重定向,也就同時(shí)使用ActionMapper的編碼方式重新進(jìn)行編碼,這就導(dǎo)致了后面在取出參數(shù)時(shí)變成亂碼,沒有具體閱讀它的源代碼,但是多次不同的編碼再想重新還原出來就有點(diǎn)麻煩了。而redirect是使用HttpServletResponse來重定向,就不存在上面的問題。最后改為redirect來重定向,結(jié)果如下:
          1<result name="myInfoSuccess" type="redirect">
          2    <param name="location">myapp_myInfo.action?msg=${msg}</param>
          3    <param name="encode">true</param>
          4</result>
                  注意:在myapp_myInfo.action對(duì)應(yīng)的Action必須對(duì)msg參數(shù)做一次轉(zhuǎn)碼,因?yàn)榍懊娴腁ction過來時(shí)就做了URL base64編碼,如果直接發(fā)給瀏覽器,就會(huì)在瀏覽器看到一串帶%的URL base64編碼字符,所以要加上
          1String msg = URLDecoder.decode(getMsg(), "UTF-8");
          2setMsg(msg);
                 把它設(shè)置回為中文,瀏覽器才能正常。

                 還得提到另一個(gè)重定向類型chain,它是Action鏈,還能維持當(dāng)前的值棧不變。不過用它重定向后,雖然跳到其它Action,但是在瀏覽器端的URL是不會(huì)變化的,這樣開頭提到的那個(gè)問題仍是無法解決的!

          posted @ 2011-01-14 16:41 寒武紀(jì) 閱讀(6773) | 評(píng)論 (3)編輯 收藏

               摘要: 項(xiàng)目中使用了FreeMarker做為視圖技術(shù),相對(duì)來說因?yàn)閒reemarker在視圖上有一些邏輯處理功能,某些地方就顯得比較方便,特別是macro的使用,當(dāng)然也不是說JSP就沒有這個(gè)功能,只是以前用JSP寫起來沒有這么順手.......  閱讀全文

          posted @ 2010-11-10 15:43 寒武紀(jì) 閱讀(3096) | 評(píng)論 (3)編輯 收藏

               摘要: 都說roller在國(guó)外是二次開發(fā)博客系統(tǒng)的首選,但是下載源代碼之后發(fā)現(xiàn)是它是基于netbeans項(xiàng)目結(jié)構(gòu)開發(fā)的。平時(shí)用習(xí)慣了Eclipse,所以想搬到MyEclipse上面,但沒有想到居然那么不容易,折騰了整整一天,才最終跑起來。網(wǎng)上的參考信息太少,大概都是那二三篇的轉(zhuǎn)貼,全部結(jié)合起來就差不多可以解決,借此總結(jié)一下,希望用到的朋友有參考作用!  閱讀全文

          posted @ 2009-11-22 21:04 寒武紀(jì) 閱讀(2259) | 評(píng)論 (2)編輯 收藏

               摘要:   閱讀全文

          posted @ 2009-10-14 17:41 寒武紀(jì) 閱讀(2178) | 評(píng)論 (1)編輯 收藏

              最近的二個(gè)項(xiàng)目,由于規(guī)模較小,都是十張表之內(nèi),而且表關(guān)聯(lián)非常少。所以用了一下iBatis做為數(shù)據(jù)庫關(guān)系映射,本著減少手寫JDBC代碼的目的,想著可以減少工作量。但是卻遇到了二個(gè)令人郁悶的問題。由于環(huán)境的限制,使用了jdk1.4.x編譯的iBatis2.3版本,沒有使用最近的。

              第一問題:  其中的一個(gè)項(xiàng)目,有一個(gè)表為它配置了sql Map的一個(gè)delete操作,非常簡(jiǎn)單,大概就是delete from xxx where id=#value#這樣的語句,然后用sqlMapClient進(jìn)行操作,日志打印完全正常,沒有報(bào)任何Exception,返回影響記錄數(shù)也是正確的。但是進(jìn)數(shù)據(jù)庫一看,巍然不動(dòng)!左查查,右查查,查不出任何毛病。更奇怪的是,數(shù)據(jù)庫表之間的所有關(guān)聯(lián)和索引全部取消,還是存在這問題。其它的三個(gè)字段比較少的表,這樣配置,同樣的api調(diào)用卻正常!這個(gè)出問題的數(shù)據(jù)庫表字段大概20+個(gè)左右。

              第二個(gè)問題:另一個(gè)項(xiàng)目,是二期重構(gòu),本來一期也不復(fù)雜,全部是使用JDBC實(shí)現(xiàn)的,只是有些表的字段太多,JDBC寫到煩,特別是處理一些NULL的插入,還有批處理時(shí)異常日志的詳細(xì)處理也有點(diǎn)煩。近期做二期升級(jí),就算采用iBatis來減少一些代碼量,于于喜涮涮地搞上去了,代碼的確減少了許多。單元測(cè)試也能通過,后來就設(shè)置了比較復(fù)雜的數(shù)據(jù)。發(fā)現(xiàn)問題的現(xiàn)場(chǎng)如下:在一個(gè)業(yè)務(wù)接口中,一個(gè)事務(wù)中包含了許多SQL操作,有delete,也有insert,大概十個(gè)sql語句左右,全部放在一個(gè)batch中執(zhí)行,整個(gè)batch提交一個(gè)事務(wù)。測(cè)試環(huán)境提供了31個(gè)類似的業(yè)務(wù)數(shù)據(jù),總共執(zhí)行31個(gè)事務(wù),采用for的循環(huán)執(zhí)行調(diào)用,每逢索引 i = 10*n  的時(shí)候就會(huì)卡住,這個(gè)操作得花很長(zhǎng)時(shí)間,最后能通過。后來進(jìn)行跟蹤,發(fā)現(xiàn)是在執(zhí)行第一個(gè)語句delete一個(gè)記錄(delete from xxx where id='xx')同樣也是單表刪除。搜索了google,baidu,沒有任何資源,翻遍了文檔沒有任何說明,查了網(wǎng)站FAQ也沒有辦法。于是,只能.......郁悶!

             為什么遇到delete都會(huì)有這個(gè)問題?不曉得有沒有高手遇到同樣的問題,這里算是總結(jié)的同時(shí)也提問,希望有遇到相同類型的高手給個(gè)解決的方案。如果不行,就得倒回去用JDBC實(shí)現(xiàn),就此iBatis的體驗(yàn)使用也就擱置,估計(jì)以后也不會(huì)碰它了。Hibernate就不用了,有點(diǎn)小題大作。
             google了才知道,原來iBatis的書籍、論壇、資料、討論等等相比Hibernate要少很多。學(xué)習(xí)是很簡(jiǎn)單,但是遇到這種細(xì)節(jié)的時(shí)候,又不太愿意花時(shí)間去研究源代碼(都是現(xiàn)實(shí)所逼,有個(gè)球時(shí)間呀?)。所以選框架要慎重!!!

          posted @ 2009-08-21 16:52 寒武紀(jì) 閱讀(4328) | 評(píng)論 (24)編輯 收藏

               摘要:   閱讀全文

          posted @ 2009-07-31 15:58 寒武紀(jì) 閱讀(2161) | 評(píng)論 (6)編輯 收藏

          Java環(huán)境安裝
              1.  從sun主頁下載JDK for Linux版本。這里下載的是jdk-6u6-linux-i586.bin。
              2. 用root用戶登錄ubuntu,或是在普通用戶下用su命令切換用戶。切換到所需的安裝目錄。類型:cd <目錄路徑名>   例如,要在 /usr/java/ 目錄中  安裝軟件,請(qǐng)鍵入:cd /usr/java/,把jdk-6u6-linux-i586.bin文件拷貝這個(gè)目錄里面,設(shè)置權(quán)限為可執(zhí)行類型:chmod a+x jre-6u6-linux-i586.bin
              3. 啟動(dòng)安裝過程。鍵入:./jre-6u<version>-linux-i586.bin。接下來會(huì)提示二進(jìn)制的許可協(xié)議,鍵入yes回車即可。安裝過程如果遇到一些問題,都同樣鍵入yes就可以。
              4. 一路下來,最后看到Done字樣,就完成了Java環(huán)境的安裝。安裝的位置就是當(dāng)前目錄 /usr/java,當(dāng)然你可以選擇在別的位置。可以用ls命令查看一下是否正常。

          環(huán)境變量配置
             上面安裝完畢后,直接在shell里面輸入java是不起作用的,需要先配置一下環(huán)境變量。一般都會(huì)用export命令,不過這樣設(shè)置只對(duì)當(dāng)前shell起作用,重啟或是切換到別的shell會(huì)話就不起作用了。可以選擇配置 .bashr文件。用vi或是gedit打開,在末尾添加下面的內(nèi)容
             export JAVA_HOME=/usr/java/jdk1.6.0_06
             export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
             export PATH=$PATH:$JAVA_HOME/bin
             然后保存。再在shell里面試驗(yàn)一下是否安裝成功。echo一下各個(gè)變量是否正常,然后輸入java -version看看。
            還有一種方式是修改/etc/profile,這樣相當(dāng)于修改系統(tǒng)配置文件,對(duì)所有用戶都有影響,我在嘗試的過程可能輸入了一些異常字符,導(dǎo)致整個(gè)ubuntu無法用GUI登錄,無奈只能用文本登錄,然后再次把profile改回來才正常。
             好了之后可以先試一下用vi新建一個(gè)Hello的Java文件,然后編譯一下試運(yùn)行。

          Eclipse安裝
             Linux下面的Eclipse在ubuntu的界面渲染下看起來非常漂亮。先從Eclipse社區(qū)下載一個(gè)Linux版本的,這里下載的是europa版本的gz包。只要解壓到一個(gè)目錄就可以,這里選擇/opt/eclipse下面。Eclipse是解壓就可以使用的。不過為了方便,我們?cè)谧烂孀鲆粋€(gè)啟動(dòng)器把啟動(dòng)目標(biāo)指向到Eclipse的安裝目的地,選擇里面已有的圖標(biāo)文件,這樣就完成了。不過默認(rèn)Eclipse是找不到Java執(zhí)行路徑的,有網(wǎng)友寫了這樣一個(gè)腳本eclipse.sh 放到/usr/local/bin目錄下,記得加上775權(quán)限。然后把啟動(dòng)器位置指到這里eclispe.sh。下面是eclipse.sh的內(nèi)容:
              #!/bin/bash
              #
              # 執(zhí)行 eclipse 2.1.1
              #
              export JAVA_HOME=/usr/java/XXX
              export CLASSPATH=/usr/java/XXX/lib
              /opt/eclipse/eclipse -vm /usr/java/XXX/bin/java -data ~/workspace &
              # -vm 參數(shù)用以指定使用哪一個(gè) jvm 來執(zhí)行Eclipse,
              # -date參數(shù)用以指定Eclipse的數(shù)據(jù)目錄。在此指定其存在用戶根目錄(~)下的workspace目錄中
            
             還有一個(gè)比較笨的方法,Eclipse默認(rèn)會(huì)去找它自己目錄下是不是有jre存在,如果有,它就可以啟動(dòng),那么你可以直接把先前安裝好的JDK里面的JRE目錄整個(gè)復(fù)制到Eclipse里面。然后就可以直接運(yùn)行了。下面看一下效果
            

          posted @ 2008-08-29 17:07 寒武紀(jì) 閱讀(38196) | 評(píng)論 (6)編輯 收藏

              ActionSet是Eclipse RCP里面一非常重要的概念,因?yàn)椴藛巍⒐ぞ邫凇⑸舷挛牟藛巍顟B(tài)欄很多操作都是共享的,所以Action就是用來處理重復(fù)出現(xiàn)的東西。至于Eclipse里面定義ActionSet有非常多的技巧,可能無法一一列舉,而且使用方法也多種多樣。下面介紹的是RssOwl2項(xiàng)目的ui源代碼部分的一小塊。
             1.  菜單的插入點(diǎn) -- GroupMarker和Separator的使用
                  ApplicationActionBarAdvisor類是定義全局所有Action插入點(diǎn)和入口,查看fillMenuBar(IMenuManager)方法,為了簡(jiǎn)化,以其中的輔助方法createFileMenu(IMenuManager)為例,講述一下實(shí)現(xiàn)菜單“文件”的內(nèi)容,先看一下菜單的結(jié)構(gòu)

                 像Close,Import...之類的非常簡(jiǎn)單,看一下它是如何實(shí)現(xiàn)New這個(gè)子菜單的。首先看一下它的源代碼如何定義插入點(diǎn)
               
          /* Menu: File */
            
          private void createFileMenu(IMenuManager menuBar) {
              MenuManager fileMenu 
          = new MenuManager("&File", IWorkbenchActionConstants.M_FILE);
              menuBar.add(fileMenu);

              fileMenu.add(
          new GroupMarker(IWorkbenchActionConstants.FILE_START));
              fileMenu.add(
          new GroupMarker(IWorkbenchActionConstants.NEW_EXT));
              fileMenu.add(
          new Separator());

              fileMenu.add(getAction(ActionFactory.CLOSE.getId()));
              fileMenu.add(getAction(ActionFactory.CLOSE_ALL.getId()));
              fileMenu.add(
          new GroupMarker(IWorkbenchActionConstants.CLOSE_EXT));
              fileMenu.add(
          new Separator());
              fileMenu.add(getAction(ActionFactory.SAVE_AS.getId()));
              fileMenu.add(
          new GroupMarker(IWorkbenchActionConstants.SAVE_EXT));
              fileMenu.add(
          new Separator());
              fileMenu.add(getAction(ActionFactory.PRINT.getId()));

              fileMenu.add(
          new Separator());
              fileMenu.add(
          new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));

              fileMenu.add(fReopenEditors); 
          // TODO Consider moving into a "Go" Menu!

              fileMenu.add(
          new Separator());
              fileMenu.add(
          new GroupMarker(IWorkbenchActionConstants.FILE_END));
              fileMenu.add(
          new Separator());

              fileMenu.add(getAction(ActionFactory.QUIT.getId()));
            }
                 其中有一行fileMenu.add(new GroupMarker(IWorkbenchActionConstants.NEW_EXT)); 這里是定義一個(gè)GroupMarker作為組標(biāo)記,把子菜單New容納進(jìn)來。這個(gè)NEW_EXT的值是:new.ext
                現(xiàn)在跳回到plugin.xml去看一下它的ActionSet定義,結(jié)構(gòu)如下:
          ,點(diǎn)擊New(menu),它的path值為:file/new.ext,這個(gè)路徑就是在createFileMenu方法定義的路徑,第一個(gè)是“File”本身的ID。也就是把子菜單New(menu)插入到指定的那個(gè)GroupMarker,ID為new.ext。然后定義了三個(gè)ID分別為bookmark,newsbin,searchmark,的groupmarker和一個(gè)folder的separator,這個(gè)三ID分別就對(duì)應(yīng)上面actionSet定義的三個(gè)action,以其中的Bookmark(action)為例,它的menubarPath為:file/new_sub/bookmark,代表插入到"File"主菜單中定義的new_sub子菜單中,new_sub是New(menu)的ID。因?yàn)閒older是定義為separator,所以它會(huì)有一條分隔線。這只是RssOwl的定義方法,其實(shí)以前自己做開發(fā)的時(shí)候是沒有這樣定義的,而且把子菜單New也寫在方法fillMenuBar中的,菜單把ID都寫在里面,ActionSet的配置就沒有子菜單出現(xiàn)了,但是這樣定義看起來就比較亂。采用這種寫法感覺比較簡(jiǎn)潔。
              2.  Action的實(shí)現(xiàn)
                 仍以bookmark為例,它的實(shí)現(xiàn)類是NewBookMarkAction,實(shí)現(xiàn)了IWorkbenchWindowActionDelegate, IObjectActionDelegate二個(gè)接口,第一個(gè)是ActionSet指定實(shí)現(xiàn)接口,第二個(gè)是對(duì)象操作菜單要求實(shí)現(xiàn)的接口(但事實(shí)發(fā)現(xiàn)沒有再定義它的配置,可能是internal版本的原因),也就是說這個(gè)Action是多功能,它將會(huì)出現(xiàn)在主菜單,工具欄,和局部的右鍵菜單上。主菜單和工具欄的位置都在ActionSet配置定義了,看看它的右鍵菜單實(shí)現(xiàn)是在哪里的,這個(gè)右鍵是在視圖Bookmarks定義的,那么跳轉(zhuǎn)到org.rssowl.ui.internal.views.explorer.BookMarkExplorer類去看看。里面有一個(gè)hookContextualMenu()方法,就是定義它的右鍵菜單的,看一下代碼實(shí)現(xiàn):
          private void hookContextualMenu() {
              MenuManager manager 
          = new MenuManager();

              
          /* New Menu */
              MenuManager newMenu 
          = new MenuManager("New");
              manager.add(newMenu);

              
          /* New BookMark */
              newMenu.add(
          new Action("Bookmark"{
                @Override
                
          public void run() {
                  IStructuredSelection selection 
          = (IStructuredSelection) fViewer.getSelection();
                  IFolder parent 
          = getParent(selection);
                  IMark position 
          = (IMark) ((selection.getFirstElement() instanceof IMark) ? selection.getFirstElement() : null);
                  
          new NewBookMarkAction(fViewSite.getShell(), parent, position).run(null);
                }


                @Override
                
          public ImageDescriptor getImageDescriptor() {
                  
          return OwlUI.BOOKMARK;
                }

              }
          );

             
          //其它定義
          }
               原來實(shí)現(xiàn)也很簡(jiǎn)單,只是往MenuManager里面添加一個(gè)Action而已,而且run方法就是直接調(diào)用定義好的NewBookMarkAction的run方法,但是把選中對(duì)象做為參數(shù)傳進(jìn)去,因?yàn)檫@個(gè)new是涉及當(dāng)前上下文選擇對(duì)象的。
              3. 下拉類型的工具按鈕定義
              非常常見的Dropdown類型的工具欄按鈕可以把功能類型的按鈕歸為一類,做成一個(gè)下拉菜單形式,有默認(rèn)的按下功能,也有可以選擇其它類似功能的下三角形式,樣子如下:

              這個(gè)dropdown的Action是定義在ActionSet配置里的。style是pulldown類型的,所以實(shí)現(xiàn)類NewTypeDropdownAction實(shí)現(xiàn)了IWorkbenchWindowPulldownDelegate接口,它的run方法就是定義默認(rèn)點(diǎn)擊不做選擇時(shí)的事情,這個(gè)下拉菜單是實(shí)現(xiàn)getMenu(Control parent)方法而來,它定義了如何生成這個(gè)菜單,這就用到了最原始的SWT中的MenuItem了,并且為它們添加SelectionListener,方法實(shí)現(xiàn),不用說都知道了,又是New一個(gè)先前定義好的NewBookMarkAction類,然后又是調(diào)用它的run方法。所以總結(jié)一下,Action的重用不一定是這個(gè)類的重用,關(guān)鍵是它的run方法的重用,在不同的場(chǎng)景下它的外在表現(xiàn)形式可能會(huì)多種多樣,但是它的run內(nèi)容是一致的。像添加這種添加的run大部分時(shí)候都是彈出一個(gè)對(duì)話框,而對(duì)話框大都又是Winzard類型的,因?yàn)閃inzard可以共享放到dialog里面。所以這種復(fù)用的思想在Eclipse里面隨處可見。
             歸結(jié)一下,其實(shí)這些技巧都是次要的,因?yàn)樽鯣UI一個(gè)比較痛苦的事情就是經(jīng)常要寫很多重復(fù)類似的代碼,抽取的不好,可能就變得不倫不類了。怎么利用它的這種思想,把復(fù)用的代碼都抽取在一起,而閱讀起來又比較輕松才是關(guān)鍵。
             知道的就這些,先介紹到這里,下次再談?wù)勂渌碌陌l(fā)現(xiàn)。

          posted @ 2008-08-21 11:29 寒武紀(jì) 閱讀(1803) | 評(píng)論 (0)編輯 收藏

          主站蜘蛛池模板: 锡林浩特市| 子长县| 兴义市| 安岳县| 克山县| 白河县| 青浦区| 神木县| 黎川县| 民勤县| 河津市| 日土县| 兴安县| 库车县| 平远县| 荥阳市| 铜山县| 南郑县| 赤城县| 陆川县| 常州市| 荥阳市| 德格县| 怀宁县| 兴仁县| 甘孜县| 桓仁| 始兴县| 泾阳县| 保山市| 库尔勒市| 深水埗区| 青田县| 漳浦县| 永丰县| 泰州市| 武平县| 深水埗区| 名山县| 闻喜县| 秦安县|