如鵬網 大學生計算機學習社區(qū)

          CowNew開源團隊

          http://www.cownew.com 郵件請聯系 about521 at 163.com

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            363 隨筆 :: 2 文章 :: 808 評論 :: 0 Trackbacks

          #

          JDBMonitor是一個開源項目。使用它開發(fā)者可以很輕松為系統(tǒng)增加數據庫執(zhí)行日志功能。它使用十分方便,您所需要做的唯一事情就是在您系統(tǒng)的JDBC連接字符串前增加類似于 "listenerconfig=/config.xml:url=" 的字符即可,不用寫任何代碼。

          使用 JDBMonitor,您可以把數據庫執(zhí)行情況記錄通過各種方式記錄下來,比如打印到控制臺、輸出到文件或者通過socket傳送給遠程客戶端。JDBMonitor是可擴展的,您可以擴展它來將執(zhí)行情況通過其他方式記錄下來,您所需要做的就是寫一個實現IDBListener接口的類即可。

          JDBMonitor遵守 GNU Lesser General Public Licence (LGPL)協(xié)議。此協(xié)議包含在發(fā)行包中。

          入門

          幾乎所有大型數據庫應用都包含有自己的SQL執(zhí)行日志功能,此功能不僅能幫助開發(fā)人員調試,而且可以為DBA(數據庫管理員)提供系統(tǒng)的運行信息。

          (1)很難將業(yè)務邏輯同日志代碼分離

          (2)降低了代碼的可讀性。

          (3)降低了系統(tǒng)的運行速度。在記錄日志的時候,程序會暫停運行等待直到記錄完成,而I/O操作是相當耗時的。

          (4)很難記錄運行耗時、語句參數等其他信息

          (5)很難為我們無法修改代碼的系統(tǒng)(例如沒有源代碼的系統(tǒng))或者很難增加記錄日志功能代碼的系統(tǒng)(比如系統(tǒng)使用了ORMapping)增加日志功能。

          JDBMonitor 則不同:

          (1)您最多只需要修改一行代碼。您需要修改的代碼就是這一行:Class.forName("com.cownew.JDBMonitor.jdbc.DBDriver") ,然后再修改一下 JDBC連接字符串,只要從 “jdbc:db2://10.74.198.247:50000/app”修改成” listenerconfig=config.xml:url= jdbc:db2://10.74.198.247:50000/app”就可以了。在您使用WebLogic ,Tomcat或其他服務器的數據源功能的時候,連修改代碼這一步都是無需的。

          (2)JDBMonitor另起一個線程來記錄SQL,所以它不會對程序運行速度有任何影響。

          (3)它是高度可擴展的,所以您可以擴展它來把執(zhí)行情況通過其他方式記錄。比如,您可以寫一個擴展類,來通過電子郵件將日志發(fā)送出去。

          取得 JDBMonitor

          JDBMonitor的最新穩(wěn)定版本可以在JDBMonitor的網站上取得:

          http://www.cownew.com/JDBMonitor

          使用 JDBMonitor

          1 將 jdbmonitor.jar放到您系統(tǒng)的類路徑下。

          2 讓系統(tǒng)加載 JDBMonitorJDBC驅動。

          這一步將會依您系統(tǒng)加載JDBC驅動的方式的不同而不同。

          (1)如果您通過代碼的形式加載JDBC驅動,例如:

          ?? Class.forName(“com.microsoft.jdbc.sqlserver.SQLServerDriver”);
          ?? Connection cn = DriverManager.getConnection(……);

          在這種情況下 ,您必須修改 “Class.forName”這一句來加載JDBMonitor的JDBC驅動(“com.cownew.JDBMonitor.jdbc.DBDriver”),而非以前的數據庫JDBC驅動。

          例如:

          Class.forName(“com.cownew.JDBMonitor.jdbc.DBDriver”);
          ?? Connection cn = DriverManager.getConnection(……);

          (2)如果您在配置文件中指定JDBC驅動,比如,數據源配置文件或者其他類似的文件。

          請修改原來的??JDBC驅動類為 “com.cownew.JDBMonitor.jdbc.DBDriver” 。

          3 讓 JDBMonitor加載能夠加載原來的JDBC驅動

          JDBMonitor的工作原理就是截獲JDBC驅動的SQL語句調用、記錄SQL語句,然后將SQL語句重新轉發(fā)給原來的JDBC驅動,所以JDBMonitor必須首先向DriverManager注冊JDBC驅動。

          原來的JDBC驅動定義在配置文件的“JdbcDrivers” 段中。
          <JdbcDrivers>
          ??? <JdbcDriver class=" com.mysql.jdbc.Driver"/>
          ? </JdbcDrivers>

          4 在原來的JDBC連接字符串前增加 JDBMonitor所需的信息。

          您所需要做的就是將” listenerconfig=<configfilepath>:url=” 增加到原來的JDBC連接字符串前?!?strong><configfilepath>”代表配置文件的路徑,下面集中路徑都是合法的:

          /com/jdbmonitor/config.xml
          com/jdbmonitor/config.xml
          c:/ jdbmonitor /config.xml

          JDBMoinitor使用getClass().getResourceAsStream加載類似于“/com/jdbmonitor/config.xml” and “com/jdbmonitor/config.xml” 的類路徑文件,使用 FileInputStream加載類似于 “c:/ jdbmonitor /config.xml”的配置文件。

          5 指定您要使用監(jiān)聽器:

          您可以把數據庫執(zhí)行情況記錄通過各種方式記錄下來,比如打印到控制臺、輸出到文件或者通過socket傳送給遠程客戶端。

          我們已經開發(fā)了如下常用的監(jiān)聽器:FileDBListener、ConsoleDBListener、 SocketDBListene、DataBaseDBListener。當然您也可以開發(fā)滿足您要求的監(jiān)聽器。
          監(jiān)聽器定義在配置文件的 “Listeners”段中:

          <Listeners>
          ??? <!--ConsoleDBListener no arguments-->
          ??? <Listener class="com.cownew.JDBMonitor.listenerImpl.ConsoleDBListener" arg=""/>
          ???
          ??? <!--the arguments of FileDBListener is the file to log the SQL statement -->
          ??? <Listener class="com.cownew.JDBMonitor.listenerImpl.FileDBListener" arg="c:/aaa.txt"/>
          ???
          ??? <!--the arguments of SocketDBListener is the bound socket port of the listener server -->
          ??? <Listener class="com.cownew.JDBMonitor.listenerImpl.SocketDBListener" arg="9527"/>
          ? </Listeners>

          搞定!啟動您的系統(tǒng)。耶!SQL語句被記錄下來了,我們可以在控制臺、文件甚至遠程監(jiān)視器中看到日志了。

          舉例

          mvnforum的例子:

          您可以從http://www.mvnForum.com得到mvnforum。我演示用的版本是1.0。

          (1)打開webapp\WEB-INF\classes\ mvncore.xml,重新配置:

          修改之前:

          <driver_class_name>com.mysql.jdbc.Driver</driver_class_name>
          <database_url>listenerconfig=c:/log/jdbmonitor/config.xml:url= jdbc:mysql://localhost/mvnforum?useUnicode=true&amp;characterEncoding=utf-8</database_url>

          修改之后:
          <driver_class_name> com.cownew.JDBMonitor.jdbc.DBDriver </driver_class_name>
          ??????? <database_url>jdbc:mysql://localhost/mvnforum?useUnicode=true&amp;characterEncoding=utf-8</database_url>

          (2)創(chuàng)建文件 c:/log/jdbmonitor/config.xml。我只想將SQL語句記錄到文本文件中,所以我做如下配置:
          <config>
          ? <Listeners>
          ??? <!--the arguments of FileDBListener is the file to log the SQL statement -->
          ??? <Listener class="com.cownew.JDBMonitor.listenerImpl.FileDBListener" arg="c:/log.txt"/>
          ? </Listeners>
          ? <JdbcDrivers>
          ??? <JdbcDriver class="com.mysql.jdbc.Driver"/>
          ? </JdbcDrivers>
          </config>
          (3) 將 jdbmonitor.jar放到webapp\WEB-INF\lib下。
          (4) 搞定!

          Jive的例子:

          您可以從http://www.jivesoftware.com得到Jive。我演示用的版本是 Jive 2.0 beta版。

          (1)打開http://localhost:8080/jive/admin/

          “jdbc” 填為:com.cownew.JDBMonitor.jdbc.DBDriver

          “server” 填為:c:/log/jdbmonitor/config.xml:url=jdbc:mysql://locahost/jive
          (2)jdbmonitor.jar放到WEB-INF\lib下
          (4) 象mvnforum中一樣創(chuàng)建同樣的 c:/log/jdbmonitor/config.xml 文件.
          (4) 搞定!

          代碼方式的例子:

          盡管直接在代碼中指定系統(tǒng)所用的JDBC驅動類名和JDBC連接字符串是不推薦的,但是仍然有系統(tǒng)是這么做的。

          比如:

          ????????????? Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
          ????????????? Connection conn = null;
          ????????????? PreparedStatement ps = null;
          ????????????? try
          ????????????? {
          ???????????????????? conn = DriverManager
          ?????????????????????????????????? .getConnection("jdbc:odbc:MQIS");
          ???????????????????? for (int i = 0; i < 1000; i++)
          ???????????????????? {???????????????????
          ??????????????????????????? ps = conn.prepareStatement("update T_Material set fid=fid");
          ??????????????????????????? ps.execute();
          ??????????????????????????? ps.close();
          ???????????????????? }
          ????????????? } finally
          ????????????? {
          ???????????????????? ....?????????
          ????????????? }

          (1)修改一下代碼為:
          ?????????????? Class.forName("com.cownew.JDBMonitor.jdbc.DBDriver");
          ????????????? Connection conn = null;
          ????????????? PreparedStatement ps = null;
          ????????????? try
          ????????????? {
          ???????????????????? conn = DriverManager.getConnection("listenerconfig= c:/log/jdbmonitor/config.xml:url=jdbc:odbc:MQIS");
          ???????????????????? for (int i = 0; i < 1000; i++)
          ???????????????????? {
          ??????????????????????????? ps = conn.prepareStatement("update T_Material set fid=fid");
          ??????????????????????????? ps.execute();
          ??????????????????????????? ps.close();
          ???????????????????? }
          ????????????? } finally
          ????????????? {
          ????????????? ?????? ....?????????
          ????????????? }

          (2)創(chuàng)建c:/log/jdbmonitor/config.xml文件。我想記錄SQL語句到文本文件中同時輸出到控制臺,這樣可以輔助我進行調試,所以我配置如下:
          <config>
          ? <Listeners>
          ??? <!--the arguments of FileDBListener is the file to log the SQL statement -->
          <Listener class="com.cownew.JDBMonitor.listenerImpl.FileDBListener" arg="c:/log.txt"/>

          <!--ConsoleDBListener no arguments-->
          <Listener class="com.cownew.JDBMonitor.listenerImpl.ConsoleDBListener" arg=""/>
          ? </Listeners>
          ? <JdbcDrivers>
          ??? <JdbcDriver class="com.mysql.jdbc.Driver"/>
          ? </JdbcDrivers>
          </config>
          (3) 將 jdbmonitor.jar放到類路徑下。
          (4) 搞定!

          監(jiān)聽器

          我們已經開發(fā)了如下常用的監(jiān)聽器:FileDBListener、ConsoleDBListener、 SocketDBListener、DataBaseDBListener。

          1、ConsoleDBListener 控制臺監(jiān)聽器

          ConsoleDBListener會將SQL語句打印到控制臺中。

          這個監(jiān)聽器很容易配置:

          <Listener class="com.cownew.JDBMonitor.listenerImpl.ConsoleDBListener" arg=""/>

          2、FileDBListener 文件監(jiān)聽器

          FileDBListener 會將SQL語句保存到文本文件中。

          如下配置:

          <Listener class="com.cownew.JDBMonitor.listenerImpl.FileDBListener" arg="c:/aaa.txt"/>

          arg="c:/aaa.txt"表示日志將保存到文件c:/aaa.txt中。

          3、SocketDBListener Socket監(jiān)聽器

          SocketDBListener是一個socket服務器,客戶端連接到它上邊以后就可以接收到它發(fā)出的SQL語句。

          如下配置:

          <Listener class="com.cownew.JDBMonitor.listenerImpl.SocketDBListener" arg="9527"/>

          arg="9527"表示服務器將在9527端口監(jiān)聽。

          我們已經開發(fā)了如下兩種客戶端:SocketConsoleClient(Socket控制臺客戶端) 和 SocketSwingClient(Socket Swing客戶端)。

          SocketConsoleClient工作在控制臺中:

          SocketSwingClient是一個Swing GUI客戶端:

          您可以運行"java -classpath jdbmonitor.jar com.cownew.JDBMonitor.listenerImpl.sckListenerClient.SocketConsoleClient" 來啟動SocketConsoleClient,運行"java -classpath jdbmonitor.jar com.cownew.JDBMonitor.listenerImpl.sckListenerClient.SocketSwingClient"啟動SocketSwingClient。

          您可以編寫符合您自己要求的客戶端,具體細節(jié)請參考com.cownew.JDBMonitor.listenerImpl.sckListenerClient.ListenerClientcom.cownew.JDBMonitor.listenerImpl.sckListenerClient.IDBSocketClientListener.

          4、DataBaseDBListener

          DataBaseDBListener將會把SQL語句記錄到數據庫中:

          如下配置:

          <Listener class="com.cownew.JDBMonitor.listenerImpl.DataBaseDBListener"
          arg="dburl=jdbc:odbc:MQIS;user=;password=;logtable=T_Log_SQLLog"/>

          "dburl=jdbc:odbc:MQIS;user=;password=;"表示目標數據庫的JDBC連接字符串。"logtable=T_Log_SQLLog" 表示SQL記錄將被保存到哪個表中,默認的是T_Log_SQLLog。

          如果目標數據庫用的JDBC驅動與被監(jiān)控的數據庫不同,請將它加入配置文件的 "JdbcDrivers" 部分,例如:

          <config>
          <Active>true</Active>
          <Listeners>

          <Listener class="com.cownew.JDBMonitor.listenerImpl.ConsoleDBListener" arg=""/>

          <Listener class="com.cownew.JDBMonitor.listenerImpl.DataBaseDBListener"
          arg="dburl=jdbc:odbc:MQIS;user=;password=;logtable=T_Log_SQLLog"/>
          </Listeners>
          <JdbcDrivers>
          <JdbcDriver class="com.microsoft.jdbc.sqlserver.SQLServerDriver"/>
          <JdbcDriver class="sun.jdbc.odbc.JdbcOdbcDriver"/>
          </JdbcDrivers>
          </config>

          "T_Log_SQLLog"的結構是:

          "T_Log_SQLLog"的建庫腳本在com/cownew/JDBMonitor/listenerImpl/dataBaseListener,(db2.sql,mssqlserver.sql,oracle.sql)。

          DataBaseDBListener是跨數據庫的,你可以把記錄SQL到任何關系數據庫中。

          FAQ:

          1 如果我暫時不想記錄SQL語句執(zhí)行怎么辦?難道我要重新修改成原來的樣子?

          答:無須如此。您只要修改config.xml,增加<Active>false</Active>到文件中即可。

          如下:

          <config>
          ? <Active> false </Active>
          ? <Listeners>
          ......
          </config>

          如何擴展JDBMonitor?

          我們已經開發(fā)了如下常用的監(jiān)聽器:FileDBListener、ConsoleDBListener、 SocketDBListener、DataBaseDBListener。當然您也可以開發(fā)滿足您要求的監(jiān)聽器。所有的監(jiān)聽器必須實現接口:com.cownew.JDBMonitor.commo. IDBListener。IDBListener有兩個方法需要實現:

          public void init(String arg);
          public void logSql(SQLInfo info);

          JDBMonitor會將配置文件中監(jiān)聽器定義中“arg”的值傳遞給 “init”方法、將代表SQL語句執(zhí)行信息的SQLInfo傳遞給“l(fā)ogSql”方法。

          更多信息請參考API文檔。

          posted @ 2006-05-14 21:42 CowNew開源團隊 閱讀(2493) | 評論 (2)編輯 收藏

          Session Fa?ade 模式

          ? J2EE 項目中 DTO 模式常常是與 Session Fa?ade 模式協(xié)作使用的。為了執(zhí)行一個業(yè)務邏輯,經常需要訪問多個服務器端對象(典型的是實體 Bean )。這樣出現的問題就是多個細粒度的對會話 Bean 和實體 Bean 的調用增加了多個網絡調用的開銷,而且還有一個問題就是業(yè)務邏輯被“下放”的客戶端,系統(tǒng)的可維護性降低。

          比如我們要修改一個托運協(xié)議單,我們首先要找到這個托運協(xié)議單,然后對其進行修改,(其中還要請求托運協(xié)議單明細,并對托運協(xié)議單明細進行修改),然后把修改后的結果發(fā)送到服務器。如下圖所示:


          ???
          在一次業(yè)務中客戶端要與服務器進行四次網絡調用。而且實體 Bean 都是具有事務性的,所以每個調用都會在服務器端產生一個獨立的事務。而且更加糟糕的是,由于每個對 EntityBean 的調用都是一個獨立的事務,那么如果在 updateConsignBillMaster 之后, updateConsignBillDetail 的時候發(fā)生了錯誤,那么就會造成數據的不一致,違反了事務的原子性。

          可見這種方式有如下的缺點:

          高昂的網絡調用開銷

          耦合性太高??蛻舳耸歉鶕掌鞫说腅ntityBean的結構寫的,如果服務器端發(fā)生了改變的話,也必須同時修改客戶端。

          復用性不好。修改托運協(xié)議單的業(yè)務邏輯被寫到了客戶端,如果以后我們要將UI由jsp網頁改成applets、或通過Power Builder等開發(fā)工具開發(fā)前臺界面等的時候,這些業(yè)務邏輯代碼就必須再此重寫。

          開發(fā)人員無法合理分工。在大型項目中,一般都是將表示層(jsp/servlet)、業(yè)務層(Session Bean)和持久層(Entity Bean)等由不同的程序員來完成。如果采用我們上邊提到的方法的話,表示層開發(fā)人員也必須同時理解業(yè)務層和持久層的實現方式,這樣加大了開發(fā)的復雜度和比較高的出錯率。

          無法保證業(yè)務的事務性。

          我們采用Session Fa ? ade模式解決這個問題:我們將實體Bean包裝在會話Bean中,客戶端只能訪問會話Bean和不能直接訪問實體Bean。

          采用這種模式后,客戶端和服務器的交互將會是這樣的:


          Session Fa
          ? ade模式對客戶端完全隱藏了存在于服務器端的對象模型,這樣也就減少了系統(tǒng)的復雜性,方便開發(fā)人員分工,前臺表示層開發(fā)人員只要調用Session Bean暴露的接口即可,如果業(yè)務層發(fā)生了變化,只要暴露的接口不變,那么表示層代碼就不用做任何改變。而且最重要的是:強制一個業(yè)務邏輯在一個網絡調用中執(zhí)行,并且使得所有對Entity Bean的訪問都放在同一個事務中,這樣就保證了方法調用的事務完整性。

          給大家推薦一篇文章
          "寂寞鴕鳥"的網上成功之道
          http://www.cownew.com/showart.asp?id=54
          posted @ 2006-03-29 00:55 CowNew開源團隊 閱讀(1284) | 評論 (1)編輯 收藏

          Decorate( 裝飾者模式 )

          裝飾者模式以對客戶端透明的方式動態(tài)的為對象增加責任。此模式提供了一個比繼承更為靈活的替代方案來擴展對象的功能,避免了繼承方法產生的類激增問題,而且更方便更改對象的責任。

          我們經常要為某一些個別的對象增加一些新的職責,并不是全部的類。例如我們系統(tǒng)留言反饋板塊中可能需要過濾用戶輸入留言中的一些詞匯(例如政治敏感詞匯、色情詞匯等)、還可能對用戶輸入留言進行一些修飾(例如對用戶輸入的 URL 自動加上超鏈接、對用戶輸入的 UBB 代碼進行轉換的)、還可能將用戶輸入的內容定時發(fā)送的網管的郵箱中等等。如果使用類繼承的方式進行設計,我們可能要設計一個接口

          BodyContentFilterIntf ,然后在由 BodyContentFilterIntf 派生出 SensitiveWordContentFilter HtmlContentFilter 、 SendEmailContentFilter 等類。但是如果還要要求同時能過濾敏感詞匯并能進行修飾、或者過濾敏感詞匯之后把用戶輸入的留言發(fā)送到網管郵箱等等,這樣就要增加 SensitiveWordHtmlContentFilter 、 SensitiveWordSendEmaillContentFilter 等類,這種方式導致了子類瀑發(fā)式的產生。

          一個靈活的方法是將過濾器嵌入另一個過濾器中,由這個過濾器來負責調用被嵌入過濾器的方法并執(zhí)行自己的過濾器方法。 我們稱這個嵌入的 過濾器 裝飾( Decorator )。 這個裝飾與過濾器接口一致 裝飾 將請求向前轉到到 另一個過濾器,并且可能能轉發(fā)前后執(zhí)行一些額外的動作(如 修飾 、 發(fā)送郵件 ),透明性使你可以遞歸的嵌套多個裝飾,從面可以添加任意多的功能。

          其實 java 中的過濾器模式應用非常多,典型的就是 IO Stream 操作。在 IO 處理中, Java 將數據抽象為流( Stream )。在 IO 庫中,最基本的是 InputStream OutputStream 兩個分別處理輸出和輸入的對象,但是在 InputStream OutputStream 中之提供了最簡單的流處理方法,只能讀入 / 寫出字符,沒有緩沖處理,無法處理文件,等等。

          LineNumberInputStream BufferInputStream 、 StringBufferInputStream 等提供各種不同服務的類只要組合起來就可以實現很多功能,如下:

          FilterInputStream myStream=new LineNumberInputStream

          ( new BufferInputStream( new StringBufferInputStream( myStringBuffer)));

          多個的 Decorator 被層疊在一起,最后得到一個功能強大的流。既能夠被緩沖,又能夠得到行數,這就是 Decorator 的威力!

          我們定義一個接口 BodyContentFilterIntf 來定義所有過濾器要實現的方法:

          public interface BodyContentFilterIntf {

          ? public String filtContent(String aContent) throws ContentFilterException;

          }

          這個接口中只有一個方法 filtContent,將要過濾的留言傳給aContent參數,filtContent對aContent進行一些處理(如裝飾URL、UBB等),然后將處理后的字符串做為返回值返回;如果留言沒有通過過濾(如含有敏感詞匯等),只要拋出自定義ContentFilterException異常即可。

          下面是一個可能的一個過濾器(保證輸入的字數多于50):

          public class LengthContentFilter

          ??? implements BodyContentFilterIntf {

          ? private BodyContentFilterIntf bodyContentFilterIntf = null;

          ? public HtmlContentFilter(BodyContentFilterIntf aFilter)

          ? {

          ??? bodyContentFilterIntf = aFilter;

          ? }

          ?

          ? public String filtContent(String aContent) throws ContentFilterException {

          ?? String l_Content = aContent;

          ?? If (bodyContentFilterIntf!=null)

          ??? _Content = bodyContentFilterIntf .filtContent(l_Content);

          if (aContent.length()<=50)

          ? throw new ContentFilterException ( 輸入的字數不能少于50! );

          ??? return aContext;

          ?

          ? }

          }

          這是另一個過濾器 偽碼 用來實現向網管郵箱發(fā)送郵件

          public class SendEmailContentFilter

          ??? implements BodyContentFilterIntf {

          ?

          ? private BodyContentFilterIntf bodyContentFilterIntf = null;

          ? public SendEmailContentFilter(BodyContentFilterIntf aFilter)

          ? {

          ??? bodyContentFilterIntf = aFilter;

          ? }

          ?

          ? public String filtContent(String aContent) throws ContentFilterException {

          ?String l_Content = aContent;

          if (bodyContentFilterIntf!=null)

          l_Content = bodyContentFilterIntf .filtContent(l_Content);

          SendEmail( webmaster@SnailWeb.com ,l_Content)

          ??? return aContext;

          ? }

          }

          當然還有 SensitiveWordContextFilter( 過濾敏感詞匯 ),HtmlContentFilter( 修飾用戶輸入留言中的超級鏈接 等。

          有了這些過濾器,我們就可以很方便的為留言版添加各種復合的過濾器。例如我們想對輸入的留言進行超鏈接修飾和過濾敏感詞匯,那么我們只要如下調用即可:

          try {

          ????????? l_Content = new HtmlContentFilter(new SensitiveWordContextFilter(null)).

          ????????? filtContent(bodyContext);

          ??? }

          ??? catch (ContentFilterException ex) {

          ????? BBSCommon.showMsgInResponse(response, ex.getMessage());

          ????? return;

          }

          我們甚至可以動態(tài)的添加不同的過濾器,例如對于會員我們要對輸入的留言進行超鏈接修飾并且將他的留言發(fā)送到網管郵箱,而對于非會員我們則要過濾他輸入的敏感詞匯并且 保證輸入的字數不少于50,我們只要如下調用即可:

          try {

          ??????? BodyContentFilterIntf bodyContentFilterIntf = null;

          ??????? bodyContentFilterIntf ?= new HtmlContentFilter(null);

          ??????? if(IsMember==true)

          ??????????????? bodyContentFilterIntf ?= new? sendEmailContentFilter ( bodyContentFilterIntf );

          else

          ??????????????? bodyContentFilterIntf ?= new? SensitiveWordContextFilter( bodyContentFilterIntf );?

          l_Content = bodyContentFilterIntf. filtContent(bodyContext);

          ??? }

          ??? catch (ContentFilterException ex) {

          ????? BBSCommon.showMsgInResponse(response, ex.getMessage());

          ????? return;

          }

          關于其他的設計模式的使用我在后續(xù)文章會繼續(xù)介紹,給大家推薦一個網站 http://www.cownew.com?,是個勵志類的,不錯,看到它的介紹很好,很多文章是從天涯搞下來的。

          posted @ 2006-03-26 01:46 CowNew開源團隊 閱讀(964) | 評論 (1)編輯 收藏

          DTO模式和SessionFacade模式的應用

          ? DTO模式

          我們的系統(tǒng)中經常需要在客戶端和服務器之間傳遞批量數據 , 例如客戶端需要顯示一個托運協(xié)議單 , 那么客戶端就要向服務器請求這個托運協(xié)議單中的所有數據 ConsignDate,StartPort,SenderName 等等 、或者客戶端需要創(chuàng)建、修改或刪除一個托運協(xié)議單。所有這些都會造成巨大數量的數據在客戶端和服務器中間交換,這通??梢酝ㄟ^兩種方法解決:(1)使用一個有很多參數的函數調用,每個數據項都作為函數的一個參數。例如

          CreateConsignBill(String aBillId, String,Date aConsignDate,String,Port StartPort,String SenderName, …… )

          UpdateConsignBill(String aBillId, String,Date aConsignDate,String,Port StartPort,String SenderName, …… )

          (2)客戶端使用許多細粒度調用與服務器交換數據。如下圖


          第一種方式性能比較高,只要在一次網絡調用中就可以完成數據傳輸,但是缺點是函數參數太多,函數將迅速失去控制,每當一個參數需要去被增加或刪除,方法簽名需要改變。;第二種方法可以保證調用的清晰性,但是最大的缺點就是性能問題,一次簡單的讀取數據就會導致大量的網絡調用,每個對服務器的調用是一個網絡調用,

          需要對返回值序列化和反序列化,當 ejb 服務器還要對每次網絡調用進行安全檢查,并且如果客戶端沒有使用 JTA 的客戶分界( client-demarcated )事務,每個方法調用可能實際上在它自己的分離的事務中執(zhí)行。用這種形式執(zhí)行多個網絡調用將導致嚴重的性能下降。

          我們的解決方案是生成一個稱為數據傳送對象( Data Transfer Object,DTO )的普通 Java 類,它代表一些服務器端數據的快照 , 該對象在一個網絡調用中封裝了批量數據。

          在一個分布式系統(tǒng)中可以把 DTO 用作讀取操作和更新操作。當一個客戶端需要更新服務器上的一些數據時,它能創(chuàng)建一個封裝所有服務器需要去更新的信息的 DTO, 并傳到服務器去處理,服務器讀取 DTO 中的數據,然后進行相應的處理。當一個客戶端需要服務器中的數據時,只要向服務器端發(fā)送一個消息,服務器將數據組裝成 DTO ,然后將此 DTO 做為消息調用的返回值返回給客戶端。

          下面時讀取數據的活動圖

          posted @ 2006-03-21 00:42 CowNew開源團隊 閱讀(2100) | 評論 (1)編輯 收藏

          Decorate(裝飾者模式)

          裝飾者模式以對客戶端透明的方式動態(tài)的為對象增加責任。此模式提供了一個比繼承更為靈活的替代方案來擴展對象的功能,避免了繼承方法產生的類激增問題,而且更方便更改對象的責任。

          我們經常要為某一些個別的對象增加一些新的職責,并不是全部的類。例如我們系統(tǒng)留言反饋板塊中可能需要過濾用戶輸入留言中的一些詞匯(例如政治敏感詞匯、色情詞匯等)、還可能對用戶輸入留言進行一些修飾(例如對用戶輸入的URL自動加上超鏈接、對用戶輸入的UBB代碼進行轉換的)、還可能將用戶輸入的內容定時發(fā)送的網管的郵箱中等等。如果使用類繼承的方式進行設計,我們可能要設計一個接口

          BodyContentFilterIntf,然后在由BodyContentFilterIntf派生出SensitiveWordContentFilter、HtmlContentFilter、SendEmailContentFilter等類。但是如果還要要求同時能過濾敏感詞匯并能進行修飾、或者過濾敏感詞匯之后把用戶輸入的留言發(fā)送到網管郵箱等等,這樣就要增加SensitiveWordHtmlContentFilter、SensitiveWordSendEmaillContentFilter等類,這種方式導致了子類瀑發(fā)式的產生。

          一個靈活的方法是將過濾器嵌入另一個過濾器中,由這個過濾器來負責調用被嵌入過濾器的方法并執(zhí)行自己的過濾器方法。我們稱這個嵌入的過濾器裝飾(Decorator)。這個裝飾與過濾器接口一致裝飾將請求向前轉到到另一個過濾器,并且可能能轉發(fā)前后執(zhí)行一些額外的動作(如修飾、發(fā)送郵件),透明性使你可以遞歸的嵌套多個裝飾,從面可以添加任意多的功能。

          其實java中的過濾器模式應用非常多,典型的就是IOStream操作。在IO處理中,Java將數據抽象為流(Stream)。在IO庫中,最基本的是InputStreamOutputStream兩個分別處理輸出和輸入的對象,但是在InputStreamOutputStream中之提供了最簡單的流處理方法,只能讀入/寫出字符,沒有緩沖處理,無法處理文件,等等。

          LineNumberInputStreamBufferInputStream、StringBufferInputStream等提供各種不同服務的類只要組合起來就可以實現很多功能,如下:

          FilterInputStream myStream=new LineNumberInputStream

          ( new BufferInputStream( new StringBufferInputStream( myStringBuffer)));

          多個的Decorator被層疊在一起,最后得到一個功能強大的流。既能夠被緩沖,又能夠得到行數,這就是Decorator的威力!

          下面是我們的類靜態(tài)圖


              我們定義一個接口
          BodyContentFilterIntf 來定義所有過濾器要實現的方法:

          public interface BodyContentFilterIntf {

            public String filtContent(String aContent) throws ContentFilterException;

          }

          這個接口中只有一個方法filtContent,將要過濾的留言傳給aContent參數,filtContent對aContent進行一些處理(如裝飾URL、UBB等),然后將處理后的字符串做為返回值返回;如果留言沒有通過過濾(如含有敏感詞匯等),只要拋出自定義ContentFilterException異常即可。

          下面是一個可能的一個過濾器(保證輸入的字數多于50):

          public class LengthContentFilter

              implements BodyContentFilterIntf {

            private BodyContentFilterIntf bodyContentFilterIntf = null;

            public HtmlContentFilter(BodyContentFilterIntf aFilter)

            {

              bodyContentFilterIntf = aFilter;

            }

           

            public String filtContent(String aContent) throws ContentFilterException {

             String l_Content = aContent;

             If (bodyContentFilterIntf!=null)

              _Content = bodyContentFilterIntf .filtContent(l_Content);

          if (aContent.length()<=50)

            throw new ContentFilterException (輸入的字數不能少于50!);

              return aContext;

           

            }

          }

          這是另一個過濾器偽碼,用來實現向網管郵箱發(fā)送郵件

          public class SendEmailContentFilter

              implements BodyContentFilterIntf {

           

            private BodyContentFilterIntf bodyContentFilterIntf = null;

            public SendEmailContentFilter(BodyContentFilterIntf aFilter)

            {

              bodyContentFilterIntf = aFilter;

            }

           

            public String filtContent(String aContent) throws ContentFilterException {

           String l_Content = aContent;

          if (bodyContentFilterIntf!=null)

          l_Content = bodyContentFilterIntf .filtContent(l_Content);

          SendEmail(webmaster@SnailWeb.com,l_Content)

              return aContext;

            }

          }

          當然還有SensitiveWordContextFilter(過濾敏感詞匯),HtmlContentFilter(修飾用戶輸入留言中的超級鏈接等。

          有了這些過濾器,我們就可以很方便的為留言版添加各種復合的過濾器。例如我們想對輸入的留言進行超鏈接修飾和過濾敏感詞匯,那么我們只要如下調用即可:

          try {

                    l_Content = new HtmlContentFilter(new SensitiveWordContextFilter(null)).

                    filtContent(bodyContext);

              }

              catch (ContentFilterException ex) {

                BBSCommon.showMsgInResponse(response, ex.getMessage());

                return;

          }

          我們甚至可以動態(tài)的添加不同的過濾器,例如對于會員我們要對輸入的留言進行超鏈接修飾并且將他的留言發(fā)送到網管郵箱,而對于非會員我們則要過濾他輸入的敏感詞匯并且保證輸入的字數不少于50,我們只要如下調用即可:

          try {

                  BodyContentFilterIntf bodyContentFilterIntf = null;

                  bodyContentFilterIntf  = new HtmlContentFilter(null);

                  if(IsMember==true)

                          bodyContentFilterIntf  = new  sendEmailContentFilter(bodyContentFilterIntf);

          else

                          bodyContentFilterIntf  = new  SensitiveWordContextFilter(bodyContentFilterIntf); 

          l_Content = bodyContentFilterIntf.filtContent(bodyContext);

              }

              catch (ContentFilterException ex) {

                BBSCommon.showMsgInResponse(response, ex.getMessage());

                return;

          }

          posted @ 2006-03-12 00:00 CowNew開源團隊 閱讀(1394) | 評論 (1)編輯 收藏

          工廠方法模式
             
          以可移植的、可擴展的方式來生成流水號EJB應用中的一個難點。 現在比較成熟的流水號生成策略有全局唯一標識(即UUID)和使用數據庫內置流水號生成策略。全局唯一標識有單件模式、根據網絡標識(Mac地址+IPJVM唯一對象標識)等策略。不同的數據庫也有不同的流水號生成策略:例如Oracle采用內置流水號產生機制,SQL Server則采用Identity機制。這給我們帶來方便的同時也使得應用程序在不同系統(tǒng)之間移植變得很麻煩。我采用工廠方法模式解決這個問題。
            
          結構圖


          我們首先定義一個基類接口,它定義了各種唯一序列生成器的共同的方法。
          abstract public interface SequenceCreator {
            abstract public String getSequenceId(String aId);
            abstract public Integer getSequenceAsInt(String aId);
          }
           
          兩個函數的參數aId是不同流水號生成標識。getSequenceId是產生以字符串形式返回的流水號,getSequenceAsInt是以整形形式返回流水號。為了防止無謂的重復,下面的實例中我們將只寫各個方法的getSequenceId實現。

          1)我們首先看SQLSequenceCreator的實現代碼

              Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

              Connection con = java.sql.DriverManager.getConnection("jdbc:odbc:DNSEJB");

              CallableStatement cs = con.prepareCall("{call SetIndex(?,?,?)}");

              cs.registerOutParameter(2,Types.VARCHAR);

              cs.setString(1,aId);

              cs.setInt(3,10);

              cs.executeUpdate();

              String str= cs.getString(2);

              return str.substring(aId.length(), str.length());

          我們是調用我們自定義的存儲過程來生成流水號的,存儲過程的代碼請參看代碼。

          2Oracle的實現代碼

          String strSQL = "select " + sequence_name + ".nextval from DUAL";

              Statement  stmt = conn.createStatement();

              ResultSet rs = stmt.executeQuery(strSQL);

              rs.next();

          return rs.getString(1);

          Oracle對流水號生成提供了比較好的支持,而且Oracle的生成策略也比SQLServer更高效,消耗更少的資源,資源鎖定情況也比SQLServer少。

          3

          UUID的實現代碼

          InetAddress inet = InetAddress .getLocalHost();

          Byte[] bytes = inet.getAddress();

          String hexInetAddress = hexFormat(getInt(bytes),8);

          String thisHashCode=hexFormat(System.identityHashCode(this),8);

          MideValue = hexInetAddress+thisHashCode;

          Seeder = new SecureRandom();

          In node = seeder.nextInt();

          Long timeNow = System.currentTimeMillis();

          Int timeLow = (int)timeNow&oxFFFFFFF;

          Int  node = seeder.nextInt();

          Return (hexFormat(timeLow,8)+mid+hexFormat(node,8));

              UUID是一個基于字符串的主鍵,他有一下字符串組合而成:利用System.currentTimeMillis()精確道毫秒的唯一、IP地址的十六進制標識、利用System.identityHashCode(this)得到的一個JVM內部的唯一地址標識和利用隨機數生成器生成隨機數。

           

          還有很多不同的流水號生成策略,我們不準備一一羅列。我們的主要問題是要解決在采用不同的序列生成策略時將代碼的修改減到最小。

          我們定義的SequenceCreator 類定義了所有流水號生成策略公共的方法,并且把這些方法定義為虛方法,在不同的流水號生成策略代碼中只要覆蓋這些方法即可。序列號生成器工廠類SequenceCreatorFactory getSquenceCreator()并不返回具體的流水號生成類,而是返回SequenceCreator,這樣當采用不同策略時只要修改getSquenceCreator方法即可。

          posted @ 2006-03-11 00:11 CowNew開源團隊 閱讀(3020) | 評論 (1)編輯 收藏

               摘要: J2EE項目風險 一、概述 當您開始著手一個企業(yè)級JAVA項目的時候,您必須對很多方面進行權衡:供應商關系,為保證完整性在設計和開發(fā)階段的過度設計。每個都會帶來與生俱來的的一些風險,其中一些是非常明顯的,而其他則不怎么明顯。但是所有這些風險都是可以避免的。在Humphrey Sheil的這篇文章中,他分析了有可能對企業(yè)級JAVA項目的成功造成威脅的10個風險,并且描述了避免方法。 在我當程序...  閱讀全文
          posted @ 2006-03-10 00:50 CowNew開源團隊 閱讀(929) | 評論 (0)編輯 收藏

            今天看了《一聲嘆息》
           今天在PPLive上重看了一遍《一聲嘆息》。其實一直都挺喜歡張國立、彪哥怕的電影的。以前的電影看了以后輕松、開心的要命,可這次卻心里一直沉重的要命。我沒有經歷過婚姻,不能完全體會其中親情與愛情的矛盾,但畢竟是已過已經有過一些人生經歷的人,也總能從電影腫體會中或對或錯的一些東西,也許過上幾年甚至幾十年再回頭看自己寫的這些東西會感覺自己很可笑,但是這些也確實是我真實的體會與感受。
            “牽著你的手,就象左手牽右手沒感覺,但砍下去也會鉆心的疼”。也許這就是妻子和情人最大的區(qū)別吧。人在這個社會上混總會有各種各樣的誘惑,人也在這些誘惑的拒絕、接受中前行。整個電影中給我印象最深的是宋曉英,她是我心目中也是很多人心目中的賢妻良母,可是當我們遇到李小丹或者王小丹的時候我們會怎么做?說實話,真不知道。宋曉英是個很傻的女人,也是個很有心計的女人。


            夫妻本是同林鳥,待到天明各自飛。
           

          posted @ 2006-03-09 00:03 CowNew開源團隊 閱讀(311) | 評論 (0)編輯 收藏

                今天改完了bug就開始考慮起怎么優(yōu)化數據導入的程序了。我們的系統(tǒng)構架要求客戶端不能執(zhí)行sql語句,所有的數據庫操作都要通過ejb來實現。我這個數據導入就麻煩了,因為是大量數據的導入,如果采用ejb的方式一條一條的導入,速度會很慢。
              所以我采取了定義一個SessionBean,在SessionBean中定義一個executeSQL方法,接受兩個參數,一個是sql語句(可能帶參數),和一個參數值數組。這樣客戶端就可以直接通過sql語句操縱數據庫了。但是問題也就來了,如果每條數據都調用executeSQL訪問一次數據庫,這樣不僅會造成頻繁的事務啟動,速度很慢,而且由于各個數據庫插入操作之間是在不同的ejb調用中進行的,所以無法保證事務。我優(yōu)化的重點當然也就在這兩點上。項目緊急,我不能對構架做太大的改動了,因為畢竟以前的實現方式可以導入數據了,我就琢磨著在不做大規(guī)模改動的情況下進行優(yōu)化,將風險降低到最小。
              系統(tǒng)中所有ejb都是基于接口的,比如上邊定義的executeSQL方法就定義在IImportDataFacade接口中,ejb實現這個接口,客戶端通過工廠方法ImportDataFactory.getInstance()訪問這個接口(ImportDataFactory.getInstance()返回IImportDataFacade類型)。好,我就從他下手。定義一個ImportDataDecorate類,讓他也實現IImportDataFacade接口,它的executeSQL實現不是吧sql提交到數據庫,而是把它緩存起來,等所有插入語句執(zhí)行完畢后將緩存的sql語句一次性提交到我定義的一個新的接口方法中(此方法含有一個sql數組的發(fā)那個法),這樣就可以保證事務和速度了。
              我是怎么實現對系統(tǒng)改動最小呢?對了,我只要把所有調用ImportDataFactory.getInstance().executeSQL()的地方替換成new ImportDataDecorate(ImportDataFactory.getInstance()).executeSQL(),哈哈,改動很小吧,而且一旦發(fā)現這種實現方式不可行,那么只要將ImportDataDecorate.executeSQL()的實現改成直接提交到數據庫就好了,其他調用根本不用變。設計模式很偉大呀,只不過我都不知道我用的這是什么模式,Proxy 還是Decorator?不知道,反正解決問題了,呵呵。
          posted @ 2006-03-07 22:08 CowNew開源團隊 閱讀(1004) | 評論 (7)編輯 收藏

          1、人為了成功 總會失去些最本性的東西
          要想得到一些東西必須要失去一些東西,只有失去一些東西才能得到一些東西,成功的人是得到的東西的價值大于失去的,失敗的人是失去的東西的價值大于得到的東西
          但是往往失去的是人性中最美好的東西  
          美好的東西不等于有價值的東西,明白這個道理,你就成熟了,但是也就“俗”了 。那些美好的東西早晚有一天要失去的,你去盡力挽留他只會使你受的傷害更多
          2、不知道從什么時候開始,再也不敢叫“男孩”“女孩”了,因為我們都已經是“人”了 。
          3、每個人女孩都是天使,除去它們那身白色羽毛,你會發(fā)現她們不過是一群吸血蝙蝠
          4、人有時流淚并不是多么愛著對方,而通常是自己感動自己,對曾經的付出的心血感到悲傷
          5、以前我以為是個文學家,等我開始每天寫blog以來我才發(fā)現,我TMD簡直就是個文學家
          posted @ 2006-03-06 23:00 CowNew開源團隊 閱讀(394) | 評論 (0)編輯 收藏

          僅列出標題
          共30頁: First 上一頁 22 23 24 25 26 27 28 29 30 下一頁 
          主站蜘蛛池模板: 栖霞市| 两当县| 大兴区| 永修县| 桓仁| 武威市| 开原市| 尉犁县| 慈溪市| 鄢陵县| 普格县| 永平县| 专栏| 额济纳旗| 鱼台县| 永州市| 峡江县| 石台县| 封丘县| 牡丹江市| 安达市| 玉门市| 鹤壁市| 综艺| 冕宁县| 灌南县| 杂多县| 永康市| 靖远县| 宜黄县| 林芝县| 义马市| 喀喇沁旗| 三台县| 武安市| 汾西县| 福清市| 南昌市| 武夷山市| 德兴市| 广饶县|