nighty

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

          2010年11月10日

          系統(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  把它示例中的配置文件拷貝過(guò)來(lái)
              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)限問(wè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
               可以看到開(kāi)頭處這樣聲明:
          ------------------------------------
          # 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方式來(lái)啟動(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,直接可以通過(guò)命令行執(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,隨便寫(xiě)點(diǎn)內(nèi)容,然后執(zhí)行些命令,看是不是B上多了這樣一個(gè)文件?如果是,則表示已經(jīng)連接成功。你接下來(lái)就可以做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ù)庫(kù)的名稱,如果沒(méi)有這一行,表示同步所有的數(shù)據(jù),另外 binlog_ignore_db = mysql。要表示忽略同步的數(shù)據(jù)庫(kù)名稱為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語(yǔ)句中,MASTER_LOG_FILE和MASTER_LOG_POS就是上面1.4中提到的show master status命令得到的結(jié)果,指定二進(jìn)制文件的名稱和開(kāi)始同步的位置。
               (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)編輯 收藏

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

               該結(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ǔ)庫(kù)初始化等等,而和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的庫(kù)(類似于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á)到在寫(xiě)和Java對(duì)接的接口時(shí),可以只聲明接口,不需要實(shí)現(xiàn)類(得減少多少重復(fù)代碼呀?),而和Java對(duì)接接口我們又可以開(kāi)發(fā)一個(gè)工具讓java code 自動(dòng)轉(zhuǎn)成 as code,如果懂得Eclipse插件開(kāi)發(fā)的話還可以進(jìn)一步做一個(gè)插件,達(dá)到Java只寫(xiě)一次就可以自動(dòng)生成對(duì)應(yīng)的flex接口,提高開(kāi)發(fā)效率。
               轉(zhuǎn)入正題,關(guān)于單元測(cè)試的概念,F(xiàn)lash Builder在4.5已經(jīng)把flex unit作為內(nèi)置庫(kù)了,這點(diǎn)和Eclipse把junit內(nèi)置類似,而flex unit的使用網(wǎng)上有大量的資料介紹,這里也不多說(shuō)。flex unit在測(cè)試as代碼還是不錯(cuò)的,和junit測(cè)試一樣,提供了一些簡(jiǎn)單的Assert斷言,但是你最痛苦的卻不是as的測(cè)試。企業(yè)開(kāi)發(fā)的特點(diǎn)就是數(shù)據(jù)量不大,但是需求坑爹,經(jīng)常變來(lái)變?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),不過(guò)如果你想純熟掌握它的整個(gè)機(jī)制,恐怕得花很多時(shí)間閱讀源代碼才行,而皮膚的制作整對(duì)別想讓美工獨(dú)自實(shí)現(xiàn),它同樣是需要技術(shù)積累的,介紹它需要用幾個(gè)篇幅才足夠。任何技術(shù)方案都一樣,BS、CS、AIR在實(shí)現(xiàn)復(fù)雜界面時(shí),對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),最痛苦的莫過(guò)于界面的單元測(cè)試。
               痛苦在哪里?回看上面那幅架構(gòu)圖,業(yè)務(wù)功能界面實(shí)現(xiàn)在Flex,業(yè)務(wù)邏輯在后臺(tái)Java,那么當(dāng)二個(gè)團(tuán)隊(duì)同時(shí)進(jìn)行工作的時(shí)候,溝通就是最大的成本。解決溝通的問(wèn)題就必須在先前設(shè)計(jì)時(shí)約定好接口和數(shù)據(jù)結(jié)構(gòu),那是會(huì)影響雙方團(tuán)隊(duì)協(xié)調(diào)的關(guān)鍵因素。當(dāng)雙方同時(shí)進(jìn)行開(kāi)發(fā)的時(shí)候,勢(shì)必存在前臺(tái)依賴后臺(tái)的情況,因?yàn)樗艿竭_(dá)界面的前提得在整個(gè)框架載入后(并且可以初始化一堆數(shù)據(jù),發(fā)生了通信),Java后臺(tái)還好說(shuō),依賴于spring和junit可以做到很好的單元測(cè)試。而flex就痛苦了,我沒(méi)有通訊啥都做不了呀!
               如何設(shè)計(jì)單元測(cè)試?最大的出發(fā)點(diǎn)就是如何切掉和后臺(tái)通信連接,看下面的簡(jiǎn)單結(jié)構(gòu)圖

               實(shí)現(xiàn)思路介紹:
               1.  簡(jiǎn)化Application加載過(guò)程   --   可以套用你主程序中的加載過(guò)程,但是不需要你的主界面其它多余的元素,達(dá)到減少到達(dá)測(cè)試界面的多余步驟(盡可能少地減少鼠標(biāo)和鍵盤操作)
               2.  定義測(cè)試配置   --   測(cè)試哪個(gè)模塊?哪個(gè)工作流程?你得通過(guò)配置的方式來(lái)定義,而不是每次都手寫(xiě)代碼,才能方便你的成員使用你這個(gè)工具
               3.  模擬后臺(tái)接口實(shí)現(xiàn)  --  記得上面說(shuō)的動(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ù)吧?沒(méi)有后臺(tái)了,提交到哪里?你得必須把你的提交對(duì)象用界面展示出來(lái)吧?好吧,這個(gè)可是個(gè)難點(diǎn)!

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

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

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

              第3模擬后臺(tái)接口實(shí)現(xiàn)。這個(gè)是比較煩的,模擬機(jī)制本身通過(guò)動(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)接口證明是沒(méi)有問(wèn)題的,那么可以混合使用,一部分調(diào)試過(guò)的接口可以直接用后臺(tái),而新接口才本地模擬。一個(gè)原則就是后臺(tái)有的,已經(jīng)證明穩(wěn)定的就用后臺(tái),沒(méi)有的或是后臺(tái)還沒(méi)有完成的你就自己模擬。

              第4對(duì)象查看器。想想flex不能操作數(shù)據(jù)庫(kù)、由于安全限制不允許直接操作文件、無(wú)法讀取本地文件目錄。而你的測(cè)試數(shù)據(jù)也許會(huì)有關(guān)聯(lián)(特別是在工作流方面),所以你得想一個(gè)方案來(lái)保存你的對(duì)象結(jié)果,而且得以一種人性化的方式查看對(duì)象內(nèi)容。且拋開(kāi)數(shù)據(jù)存儲(chǔ)的問(wèn)題,這個(gè)對(duì)象查看器如何設(shè)計(jì)就夠你頭疼的了,首先是對(duì)象得定義成一種格式,一種人可以看得懂的格式,比如xml,可以支持序列化和反序列化,你得去掉多余的無(wú)用屬性和訪問(wèn)器。又得回到反射機(jī)制上了,序列化其實(shí)不難,難的是反序列化時(shí)如何正確地轉(zhuǎn)成原來(lái)的對(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)過(guò)實(shí)踐的擴(kuò)展,把樹(shù)視圖換成表格樹(shù)(這種控件原生沒(méi)有,有第三方的可以拿來(lái)修改),看個(gè)樣圖吧!
           
               因?yàn)槟汴P(guān)注的對(duì)象內(nèi)容無(wú)法就是這三個(gè)方面,屬性名、值、和類型,又支持以樹(shù)方式導(dǎo)航對(duì)象,已經(jīng)足夠你人眼分辯內(nèi)容了。至于如何有效的保存測(cè)試數(shù)據(jù),并且組織好結(jié)構(gòu),這個(gè)方面我目前也仍在思考中,未有較好的思路。
               以上內(nèi)容僅是出于本人的一種方案,也許有更好的實(shí)現(xiàn)方案,只是水平不足以超過(guò)這種認(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)用開(kāi)發(fā)的時(shí)候,往往會(huì)用一個(gè)固定欄,比如說(shuō)類似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ù)上面的注釋,感覺(jué)應(yīng)該原因在它的皮膚,得跟進(jìn)代碼,看一下究竟。
                首先找到<s:Application>控件的標(biāo)準(zhǔn)Skin,可以在Flash Builder中直接查看。
                
                雙擊打開(kāi)ApplicationSkin,里面的代碼包含了各種SVG圖形學(xué)的實(shí)現(xiàn)api調(diào)用,flex管這些庫(kù)叫FVG,大意就是SVG的Flex實(shí)現(xiàn)版本,該庫(kù)實(shí)現(xiàn)得還算簡(jiǎn)潔!
                Application標(biāo)準(zhǔn)皮膚的就是先畫(huà)一個(gè)矩形,然后用一個(gè)Group來(lái)包含不同的形狀,最后一段<s:Group id="contentGroup" width="100%" height="100%" minWidth="0" minHeight="0" />,代表Application的內(nèi)容區(qū)域,F(xiàn)lex從設(shè)計(jì)上區(qū)分了控件樹(shù)和布局樹(shù),有些復(fù)雜,可以參考官方的文檔。因?yàn)锳pplication是屬于容器,所以必須在皮膚中包含這一句,不然程序運(yùn)行時(shí)就看不到它包含的子控件。
               而前面長(zhǎng)長(zhǎng)一串<s:Group id="topGroup">中是畫(huà)了頂欄的外觀,它用FVG庫(kù)畫(huà)了四層:
               <!-- layer 0: control bar highlight -->     底部高亮線(用畫(huà)筆填充一個(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)原理,具體篇幅就不描述。
               那么回到最初的問(wèn)題:我想改變controlBar的位置在下方怎么處理?
               從上面的分析可知其實(shí)controlBar的擺放位置是在Skin中定義的,而它是什么布局,它顯示不顯示是通過(guò)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,或是通過(guò)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),但是它從根本上是屬于布局樹(shù)中contentGroup的內(nèi)容,會(huì)受限于布局。
               Spark Skin的設(shè)計(jì)的確有高明的地方,對(duì)比Flex3的外觀,它提供給設(shè)計(jì)人員的空間實(shí)在大得多,你可以組合圖片和FVG庫(kù)的功能自定義各種外觀,當(dāng)然,我就建議你多熟悉一下FVG庫(kù)的應(yīng)用,畢竟從外加載圖片對(duì)flex來(lái)說(shuō)是一個(gè)消耗,你的程序也會(huì)增肥。

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

                問(wèn)題描述:
                    服務(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í)候沒(méi)有裝上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ù),再訪問(wèn)就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上面了,最新的官方編輯器仍在開(kāi)發(fā)階段,有網(wǎng)友貌似知道最新的源代碼鏈接位置,不過(guò)肯定是不穩(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軟件不能訪問(wèn),查看原因是CentOS的PCRE模塊未用utf-8編碼引起的,由是搜索了一些資料照著變更,沒(méi)有效果。
                 當(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í)也沒(méi)有多想就直接回車!結(jié)果------悲劇了,屏幕狂刷,我意識(shí)到pcre是基礎(chǔ)模塊,所有關(guān)聯(lián)它的或是它關(guān)聯(lián)都刪除掉,系統(tǒng)將遭遇一個(gè)災(zāi)難式的破壞。等刷完屏幕的字符,最終發(fā)現(xiàn):所有的bin目錄下的命令全部不見(jiàn)了!!!天哪,連ls命令都沒(méi)有,唯獨(dú)剩下一個(gè)cd命令。幸運(yùn)的是,當(dāng)時(shí)運(yùn)行在服務(wù)器的幾個(gè)應(yīng)用還能訪問(wèn),比如說(shuō)phpmyadmin。

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

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

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

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

                 第三天。就著原來(lái)的思路,重裝恢復(fù)到以前的版本,又用yum的原來(lái)方式裝回原來(lái)的版本,這次進(jìn)行順利。用了二個(gè)小時(shí),就把常用的軟件恢復(fù)了,同時(shí)恢復(fù)了數(shù)據(jù)庫(kù)。最后只剩下一個(gè)問(wèn)題,redmine的密鑰恢復(fù)后,仍然無(wú)法連接原來(lái)的用戶密碼,我想可能得去查閱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),如果沒(méi)有異常,恭喜,已經(jīng)安裝完成,剩下的工作就是配置一個(gè)虛擬主機(jī)和子給你的redmine,這樣可以轉(zhuǎn)到更為常用的80端口上。

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

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

                     解釋一下過(guò)程大致是先創(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>

           最后四句帶#注釋說(shuō)明是重點(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)換。有寫(xiě)過(guò)一篇文章說(shuō)MapHandler方式的一個(gè)缺陷:關(guān)于commons dbutils組件的一個(gè)小缺陷分析 ,用這種方式,在項(xiàng)目不大的情況下,寫(xiě)一些Map到JavaBean的轉(zhuǎn)換代碼工作量不大,但是在數(shù)據(jù)庫(kù)表過(guò)多并且表中的字段過(guò)多的情況下,這種重復(fù)的setter感覺(jué)有點(diǎn)煩。于是又重新思考了BeanHandler和BeanListHandler的情況,dbutils底層映射用的反射,性能上肯定有損失,不過(guò)在大多數(shù)項(xiàng)目規(guī)模不是很大的情況下,這點(diǎn)損失可以忽略,帶來(lái)的代碼減少卻是比較可觀。
                  問(wè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ù)庫(kù)的字段定義一致。Java的命名習(xí)慣一般是駱?lè)鍖?xiě)法,例如userId,那么數(shù)據(jù)庫(kù)中就必須定義為userId,而問(wèn)題在于:有時(shí)候我們需要數(shù)據(jù)庫(kù)中字段的定義格式與JavaBean的命名不一樣,比如數(shù)據(jù)庫(kù)定義為:user_id,而JavaBean則定義為userId
                  看源代碼可能有點(diǎn)費(fèi)時(shí)間,在官方的example頁(yè)面的最下面果然有一段關(guān)于自定義BeanProcessor的指引。摘錄出來(lái):

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

          如果沒(méi)有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)橹苯油ㄟ^(guò)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安裝,然后通過(guò)gem本身的更新來(lái)實(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ù)庫(kù)連接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ù)庫(kù)要預(yù)先創(chuàng)建好,如果你裝好了mysql,直接運(yùn)行mysql -uroot -p 登錄,然后create database redmine,主機(jī)名、用戶名、密碼也要寫(xiě)對(duì)。

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

           rake config/initializers/session_store.rb

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

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

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

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

          ruby script/server -e production &

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

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

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

          打開(kāi)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的安裝,過(guò)程比較繁瑣,主要是各種組件和模塊的版本匹配問(wèn)題。大多數(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方式來(lái)轉(zhuǎn)換數(shù)據(jù),當(dāng)然就是自己寫(xiě)的代碼多一點(diǎn),也無(wú)所謂。一般的查詢、子查詢、聯(lián)合查詢、包括視圖查詢等等都很正常,但是發(fā)現(xiàn)一個(gè)比較小的問(wèn)題,就是在使用聚合函數(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),這種寫(xiě)法當(dāng)然是不太好的,容易出問(wèn)題。
                  鑒于前面沒(méi)有時(shí)間了解,就都粗略使用了上面那種粗暴的map.get("")來(lái)處理,最好的情況是讓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,忽略鍵大小寫(xiě)的K-V字典,但是key使用的是ResultSetMetaData.getColumnName(),我想問(wèn)題大概出在這里,于是認(rèn)真翻了翻java的api文檔(開(kāi)發(fā)做久了容易遺忘基礎(chǔ)),果然,原來(lái)getColumnName()是:獲取指定列的名稱;而as關(guān)鍵字之后,使列名稱變成用于顯示的意義,這個(gè)時(shí)候應(yīng)該使用getColumnLabel():獲取用于打印輸出和顯示的指定列的建議標(biāo)題。建議標(biāo)題通常由 SQL AS 子句來(lái)指定。如果未指定 SQL AS,則從 getColumnLabel 返回的值將和 getColumnName 方法返回的值相同。自己手動(dòng)試驗(yàn)了一下,果然如所料,問(wèn)題就出在這里。
                  所以呢,如果想要dbutils在自動(dòng)轉(zhuǎn)換Map及MapList時(shí)能識(shí)別聚合函數(shù)的列名,那么最好的做法就是重載這種方式,懶一點(diǎn)的,你就干脆修改上面那段代碼,讓它判斷是否使用了as關(guān)鍵字。個(gè)人暫時(shí)搞不清楚官方為什么沒(méi)有考慮這一步,有時(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ì)話框問(wèn)你是不是要重新提交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è)置。
              問(wèn)題來(lái)了,重定向到myapp_myInfo.action時(shí),這個(gè)Action里面取出msg參數(shù)時(shí)變成亂碼!!! 不論中文或是英語(yǔ)還是數(shù)字,全是亂碼,折騰了一翻,URLEncoderURLDEncoder進(jìn)行URL Base64編碼和解碼處理,包括new String(msg.getBtye("ISO-8859-1"), "UTF-8")這種處理方式仍無(wú)法奏效。google了一下并且抱起書(shū)本認(rèn)真看了看struts2重定向問(wèn)題后。大概有了個(gè)思路。
              所有的重定向操作都會(huì)丟失所有的請(qǐng)求參數(shù)、請(qǐng)求屬性等,當(dāng)然包括Action的處理結(jié)果也會(huì)丟失。 
              首先搞清楚redirect、redirectAction的區(qū)別:
              1. redirect類型struts2是調(diào)用HttpServletResponse的sendRedirect(String)方法來(lái)重定向到指定的資源,可以是一個(gè)視圖結(jié)果,也可以是其它類型的Action;
              2. redirectAction同樣是重新生成一個(gè)全新的請(qǐng)求。但是struts2內(nèi)部卻是使用ActionMapperFactory提供的ActionMapper來(lái)重定向,它只能跳轉(zhuǎn)到另外一個(gè)Action;

                   由于redirectAction使用的是ActionMapper來(lái)重定向,也就同時(shí)使用ActionMapper的編碼方式重新進(jìn)行編碼,這就導(dǎo)致了后面在取出參數(shù)時(shí)變成亂碼,沒(méi)有具體閱讀它的源代碼,但是多次不同的編碼再想重新還原出來(lái)就有點(diǎn)麻煩了。而redirect是使用HttpServletResponse來(lái)重定向,就不存在上面的問(wèn)題。最后改為redirect來(lái)重定向,結(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過(guò)來(lái)時(shí)就做了URL base64編碼,如果直接發(fā)給瀏覽器,就會(huì)在瀏覽器看到一串帶%的URL base64編碼字符,所以要加上
          1String msg = URLDecoder.decode(getMsg(), "UTF-8");
          2setMsg(msg);
                 把它設(shè)置回為中文,瀏覽器才能正常。

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

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

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

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

          主站蜘蛛池模板: 邢台县| 达孜县| 利川市| 湛江市| 诸暨市| 武鸣县| 嘉峪关市| 荆门市| 十堰市| 保山市| 保靖县| 信丰县| 新绛县| 建水县| 日照市| 灵璧县| 柯坪县| 洪湖市| 石林| 修文县| 福建省| 吉隆县| 隆化县| 宁陵县| 通化县| 湘潭市| 莫力| 文登市| 三亚市| 家居| 霍山县| 铅山县| 宜昌市| 巴中市| 化州市| 瑞金市| 穆棱市| 隆林| 昂仁县| 广元市| 建湖县|