如鹏网 大学生计算机学习社区

CowNew开源团队

http://www.cownew.com 邮件请联系 about521 at 163.com

  语源科技BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  363 随笔 :: 2 文章 :: 808 评论 :: 0 Trackbacks

#

JDBMonitor是一个开源项目。使用它开发者可以很轻松为系统增加数据库执行日志功能。它使用十分方便,您所需要做的唯一事情就是在您系统的JDBC连接字符串前增加类似于 "listenerconfig=/config.xml:url=" 的字符即可,不用写任何代码。

使用 JDBMonitor,您可以把数据库执行情况记录通过各种方式记录下来,比如打印到控制台、输出到文件或者通过socket传送给远程客户端。JDBMonitor是可扩展的,您可以扩展它来将执行情况通过其他方式记录下来,您所需要做的就是写一个实现IDBListener接口的类即可。

JDBMonitor遵守 GNU Lesser General Public Licence (LGPL)协议。此协议包含在发行包中。

入门

几乎所有大型数据库应用都包含有自己的SQL执行日志功能,此功能不仅能帮助开发人员调试,而且可以为DBA(数据库管理员)提供系统的运行信息。

(1)很难将业务逻辑同日志代码分离

(2)降低了代码的可读性。

(3)降低了系统的运行速度。在记录日志的时候,程序会暂停运行等待直到记录完成,而I/O操作是相当耗时的。

(4)很难记录运行耗时、语句参数等其他信息

(5)很难为我们无法修改代码的系统(例如没有源代码的系统)或者很难增加记录日志功能代码的系统(比如系统使用了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)它是高度可扩展的,所以您可以扩展它来把执行情况通过其他方式记录。比如,您可以写一个扩展类,来通过电子邮件将日志发送出去。

取得 JDBMonitor

JDBMonitor的最新稳定版本可以在JDBMonitor的网站上取得:

http://www.cownew.com/JDBMonitor

使用 JDBMonitor

1 将 jdbmonitor.jar放到您系统的类路径下。

2 让系统加载 JDBMonitor的JDBC驱动。

这一步将会依您系统加载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语句重新转发给原来的JDBC驱动,所以JDBMonitor必须首先向DriverManager注册JDBC驱动。

原来的JDBC驱动定义在配置文件的“JdbcDrivers” 段中。
<JdbcDrivers>
    <JdbcDriver class=" com.mysql.jdbc.Driver"/>
  </JdbcDrivers>

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

您所需要做的就是将” listenerconfig=<configfilepath>:url=” 增加到原来的JDBC连接字符串前。“<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 指定您要使用监听器:

您可以把数据库执行情况记录通过各种方式记录下来,比如打印到控制台、输出到文件或者通过socket传送给远程客户端。

我们已经开发了如下常用的监听器:FileDBListener、ConsoleDBListener、 SocketDBListene、DataBaseDBListener。当然您也可以开发满足您要求的监听器。
监听器定义在配置文件的 “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>

搞定!启动您的系统。耶!SQL语句被记录下来了,我们可以在控制台、文件甚至远程监视器中看到日志了。

举例

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)创建文件 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中一样创建同样的 c:/log/jdbmonitor/config.xml 文件.
(4) 搞定!

代码方式的例子:

尽管直接在代码中指定系统所用的JDBC驱动类名和JDBC连接字符串是不推荐的,但是仍然有系统是这么做的。

比如:

              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)创建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) 搞定!

监听器

我们已经开发了如下常用的监听器:FileDBListener、ConsoleDBListener、 SocketDBListener、DataBaseDBListener。

1、ConsoleDBListener 控制台监听器

ConsoleDBListener会将SQL语句打印到控制台中。

这个监听器很容易配置:

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

2、FileDBListener 文件监听器

FileDBListener 会将SQL语句保存到文本文件中。

如下配置:

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

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

3、SocketDBListener Socket监听器

SocketDBListener是一个socket服务器,客户端连接到它上边以后就可以接收到它发出的SQL语句。

如下配置:

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

arg="9527"表示服务器将在9527端口监听。

我们已经开发了如下两种客户端: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。

您可以编写符合您自己要求的客户端,具体细节请参考com.cownew.JDBMonitor.listenerImpl.sckListenerClient.ListenerClient和com.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驱动与被监控的数据库不同,请将它加入配置文件的 "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语句执行怎么办?难道我要重新修改成原来的样子?

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

如下:

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

如何扩展JDBMonitor?

我们已经开发了如下常用的监听器:FileDBListener、ConsoleDBListener、 SocketDBListener、DataBaseDBListener。当然您也可以开发满足您要求的监听器。所有的监听器必须实现接口:com.cownew.JDBMonitor.commo. IDBListener。IDBListener有两个方法需要实现:

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

JDBMonitor会将配置文件中监听器定义中“arg”的值传递给 “init”方法、将代表SQL语句执行信息的SQLInfo传递给“logSql”方法。

更多信息请参考API文档。

posted @ 2006-05-14 21:42 CowNew开源团队 阅读(2522) | 评论 (2) | 编辑 收藏

Session Façade 模式

  在 J2EE 项目中 DTO 模式常常是与 Session Façade 模式协作使用的。为了执行一个业务逻辑,经常需要访问多个服务器端对象(典型的是实体 Bean )。这样出现的问题就是多个细粒度的对会话 Bean 和实体 Bean 的调用增加了多个网络调用的开销,而且还有一个问题就是业务逻辑被“下放”的客户端,系统的可维护性降低。

比如我们要修改一个托运协议单,我们首先要找到这个托运协议单,然后对其进行修改,(其中还要请求托运协议单明细,并对托运协议单明细进行修改),然后把修改后的结果发送到服务器。如下图所示:


   
在一次业务中客户端要与服务器进行四次网络调用。而且实体 Bean 都是具有事务性的,所以每个调用都会在服务器端产生一个独立的事务。而且更加糟糕的是,由于每个对 EntityBean 的调用都是一个独立的事务,那么如果在 updateConsignBillMaster 之后, updateConsignBillDetail 的时候发生了错误,那么就会造成数据的不一致,违反了事务的原子性。

可见这种方式有如下的缺点:

① 高昂的网络调用开销

② 耦合性太高。客户端是根据服务器端的EntityBean的结构写的,如果服务器端发生了改变的话,也必须同时修改客户端。

③ 复用性不好。修改托运协议单的业务逻辑被写到了客户端,如果以后我们要将UI由jsp网页改成applets、或通过Power Builder等开发工具开发前台界面等的时候,这些业务逻辑代码就必须再此重写。

④ 开发人员无法合理分工。在大型项目中,一般都是将表示层(jsp/servlet)、业务层(Session Bean)和持久层(Entity Bean)等由不同的程序员来完成。如果采用我们上边提到的方法的话,表示层开发人员也必须同时理解业务层和持久层的实现方式,这样加大了开发的复杂度和比较高的出错率。

⑤ 无法保证业务的事务性。

我们采用Session Fa ç ade模式解决这个问题:我们将实体Bean包装在会话Bean中,客户端只能访问会话Bean和不能直接访问实体Bean。

采用这种模式后,客户端和服务器的交互将会是这样的:


Session Fa
ç ade模式对客户端完全隐藏了存在于服务器端的对象模型,这样也就减少了系统的复杂性,方便开发人员分工,前台表示层开发人员只要调用Session Bean暴露的接口即可,如果业务层发生了变化,只要暴露的接口不变,那么表示层代码就不用做任何改变。而且最重要的是:强制一个业务逻辑在一个网络调用中执行,并且使得所有对Entity Bean的访问都放在同一个事务中,这样就保证了方法调用的事务完整性。

给大家推荐一篇文章
"寂寞鸵鸟"的网上成功之道
http://www.cownew.com/showart.asp?id=54
posted @ 2006-03-29 00:55 CowNew开源团队 阅读(1305) | 评论 (1) | 编辑 收藏

Decorate( 装饰者模式 )

装饰者模式以对客户端透明的方式动态的为对象增加责任。此模式提供了一个比继承更为灵活的替代方案来扩展对象的功能,避免了继承方法产生的类激增问题,而且更方便更改对象的责任。

我们经常要为某一些个别的对象增加一些新的职责,并不是全部的类。例如我们系统留言反馈板块中可能需要过滤用户输入留言中的一些词汇(例如政治敏感词汇、色情词汇等)、还可能对用户输入留言进行一些修饰(例如对用户输入的 URL 自动加上超链接、对用户输入的 UBB 代码进行转换的)、还可能将用户输入的内容定时发送的网管的邮箱中等等。如果使用类继承的方式进行设计,我们可能要设计一个接口

BodyContentFilterIntf ,然后在由 BodyContentFilterIntf 派生出 SensitiveWordContentFilter 、 HtmlContentFilter 、 SendEmailContentFilter 等类。但是如果还要要求同时能过滤敏感词汇并能进行修饰、或者过滤敏感词汇之后把用户输入的留言发送到网管邮箱等等,这样就要增加 SensitiveWordHtmlContentFilter 、 SensitiveWordSendEmaillContentFilter 等类,这种方式导致了子类瀑发式的产生。

一个灵活的方法是将过滤器嵌入另一个过滤器中,由这个过滤器来负责调用被嵌入过滤器的方法并执行自己的过滤器方法。 我们称这个嵌入的 过滤器 为装饰( Decorator )。 这个装饰与过滤器接口一致 。 装饰 将请求向前转到到 另一个过滤器,并且可能能转发前后执行一些额外的动作(如 修饰 、 发送邮件 ),透明性使你可以递归的嵌套多个装饰,从面可以添加任意多的功能。

其实 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;

 

  }

}

这是另一个过滤器 ( 伪码 , 用来实现向网管邮箱发送邮件 )

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;

}

我们甚至可以动态的添加不同的过滤器,例如对于会员我们要对输入的留言进行超链接修饰并且将他的留言发送到网管邮箱,而对于非会员我们则要过滤他输入的敏感词汇并且 保证输入的字数不少于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;

}

关于其他的设计模式的使用我在后续文章会继续介绍,给大家推荐一个网站 http://www.cownew.com ,是个励志类的,不错,看到它的介绍很好,很多文章是从天涯搞下来的。

posted @ 2006-03-26 01:46 CowNew开源团队 阅读(1020) | 评论 (1) | 编辑 收藏

DTO模式和SessionFacade模式的应用

  ( 一 ) DTO模式

我们的系统中经常需要在客户端和服务器之间传递批量数据 , 例如客户端需要显示一个托运协议单 , 那么客户端就要向服务器请求这个托运协议单中的所有数据 ( 如 ConsignDate,StartPort,SenderName 等等 ) 、或者客户端需要创建、修改或删除一个托运协议单。所有这些都会造成巨大数量的数据在客户端和服务器中间交换,这通常可以通过两种方法解决:(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 )事务,每个方法调用可能实际上在它自己的分离的事务中执行。用这种形式执行多个网络调用将导致严重的性能下降。

我们的解决方案是生成一个称为数据传送对象( Data Transfer Object,DTO )的普通 Java 类,它代表一些服务器端数据的快照 , 该对象在一个网络调用中封装了批量数据。

在一个分布式系统中可以把 DTO 用作读取操作和更新操作。当一个客户端需要更新服务器上的一些数据时,它能创建一个封装所有服务器需要去更新的信息的 DTO, 并传到服务器去处理,服务器读取 DTO 中的数据,然后进行相应的处理。当一个客户端需要服务器中的数据时,只要向服务器端发送一个消息,服务器将数据组装成 DTO ,然后将此 DTO 做为消息调用的返回值返回给客户端。

下面时读取数据的活动图

posted @ 2006-03-21 00:42 CowNew开源团队 阅读(2114) | 评论 (1) | 编辑 收藏

Decorate(装饰者模式)

装饰者模式以对客户端透明的方式动态的为对象增加责任。此模式提供了一个比继承更为灵活的替代方案来扩展对象的功能,避免了继承方法产生的类激增问题,而且更方便更改对象的责任。

我们经常要为某一些个别的对象增加一些新的职责,并不是全部的类。例如我们系统留言反馈板块中可能需要过滤用户输入留言中的一些词汇(例如政治敏感词汇、色情词汇等)、还可能对用户输入留言进行一些修饰(例如对用户输入的URL自动加上超链接、对用户输入的UBB代码进行转换的)、还可能将用户输入的内容定时发送的网管的邮箱中等等。如果使用类继承的方式进行设计,我们可能要设计一个接口

BodyContentFilterIntf,然后在由BodyContentFilterIntf派生出SensitiveWordContentFilter、HtmlContentFilter、SendEmailContentFilter等类。但是如果还要要求同时能过滤敏感词汇并能进行修饰、或者过滤敏感词汇之后把用户输入的留言发送到网管邮箱等等,这样就要增加SensitiveWordHtmlContentFilter、SensitiveWordSendEmaillContentFilter等类,这种方式导致了子类瀑发式的产生。

一个灵活的方法是将过滤器嵌入另一个过滤器中,由这个过滤器来负责调用被嵌入过滤器的方法并执行自己的过滤器方法。我们称这个嵌入的过滤器为装饰(Decorator)。这个装饰与过滤器接口一致。装饰将请求向前转到到另一个过滤器,并且可能能转发前后执行一些额外的动作(如修饰、发送邮件),透明性使你可以递归的嵌套多个装饰,从面可以添加任意多的功能。

其实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;

 

  }

}

这是另一个过滤器(伪码,用来实现向网管邮箱发送邮件)

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;

}

我们甚至可以动态的添加不同的过滤器,例如对于会员我们要对输入的留言进行超链接修饰并且将他的留言发送到网管邮箱,而对于非会员我们则要过滤他输入的敏感词汇并且保证输入的字数不少于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开源团队 阅读(1432) | 评论 (1) | 编辑 收藏

工厂方法模式
   
以可移植的、可扩展的方式来生成流水号EJB应用中的一个难点。 现在比较成熟的流水号生成策略有全局唯一标识(即UUID)和使用数据库内置流水号生成策略。全局唯一标识有单件模式、根据网络标识(Mac地址+IP+JVM唯一对象标识)等策略。不同的数据库也有不同的流水号生成策略:例如Oracle采用内置流水号产生机制,SQL Server则采用Identity机制。这给我们带来方便的同时也使得应用程序在不同系统之间移植变得很麻烦。我采用工厂方法模式解决这个问题。
  
结构图


我们首先定义一个基类接口,它定义了各种唯一序列生成器的共同的方法。
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());

我们是调用我们自定义的存储过程来生成流水号的,存储过程的代码请参看代码。

(2)Oracle的实现代码

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开源团队 阅读(3040) | 评论 (1) | 编辑 收藏

     摘要: J2EE项目风险 一、概述 当您开始着手一个企业级JAVA项目的时候,您必须对很多方面进行权衡:供应商关系,为保证完整性在设计和开发阶段的过度设计。每个都会带来与生俱来的的一些风险,其中一些是非常明显的,而其他则不怎么明显。但是所有这些风险都是可以避免的。在Humphrey Sheil的这篇文章中,他分析了有可能对企业级JAVA项目的成功造成威胁的10个风险,并且描述了避免方法。 在我当程序...  阅读全文
posted @ 2006-03-10 00:50 CowNew开源团队 阅读(971) | 评论 (0) | 编辑 收藏

  今天看了《一声叹息》
 今天在PPLive上重看了一遍《一声叹息》。其实一直都挺喜欢张国立、彪哥怕的电影的。以前的电影看了以后轻松、开心的要命,可这次却心里一直沉重的要命。我没有经历过婚姻,不能完全体会其中亲情与爱情的矛盾,但毕竟是已过已经有过一些人生经历的人,也总能从电影肿体会中或对或错的一些东西,也许过上几年甚至几十年再回头看自己写的这些东西会感觉自己很可笑,但是这些也确实是我真实的体会与感受。
  “牵着你的手,就象左手牵右手没感觉,但砍下去也会钻心的疼”。也许这就是妻子和情人最大的区别吧。人在这个社会上混总会有各种各样的诱惑,人也在这些诱惑的拒绝、接受中前行。整个电影中给我印象最深的是宋晓英,她是我心目中也是很多人心目中的贤妻良母,可是当我们遇到李小丹或者王小丹的时候我们会怎么做?说实话,真不知道。宋晓英是个很傻的女人,也是个很有心计的女人。


  夫妻本是同林鸟,待到天明各自飞。
 

posted @ 2006-03-09 00:03 CowNew开源团队 阅读(330) | 评论 (0) | 编辑 收藏

      今天改完了bug就开始考虑起怎么优化数据导入的程序了。我们的系统构架要求客户端不能执行sql语句,所有的数据库操作都要通过ejb来实现。我这个数据导入就麻烦了,因为是大量数据的导入,如果采用ejb的方式一条一条的导入,速度会很慢。
    所以我采取了定义一个SessionBean,在SessionBean中定义一个executeSQL方法,接受两个参数,一个是sql语句(可能带参数),和一个参数值数组。这样客户端就可以直接通过sql语句操纵数据库了。但是问题也就来了,如果每条数据都调用executeSQL访问一次数据库,这样不仅会造成频繁的事务启动,速度很慢,而且由于各个数据库插入操作之间是在不同的ejb调用中进行的,所以无法保证事务。我优化的重点当然也就在这两点上。项目紧急,我不能对构架做太大的改动了,因为毕竟以前的实现方式可以导入数据了,我就琢磨着在不做大规模改动的情况下进行优化,将风险降低到最小。
    系统中所有ejb都是基于接口的,比如上边定义的executeSQL方法就定义在IImportDataFacade接口中,ejb实现这个接口,客户端通过工厂方法ImportDataFactory.getInstance()访问这个接口(ImportDataFactory.getInstance()返回IImportDataFacade类型)。好,我就从他下手。定义一个ImportDataDecorate类,让他也实现IImportDataFacade接口,它的executeSQL实现不是吧sql提交到数据库,而是把它缓存起来,等所有插入语句执行完毕后将缓存的sql语句一次性提交到我定义的一个新的接口方法中(此方法含有一个sql数组的发那个法),这样就可以保证事务和速度了。
    我是怎么实现对系统改动最小呢?对了,我只要把所有调用ImportDataFactory.getInstance().executeSQL()的地方替换成new ImportDataDecorate(ImportDataFactory.getInstance()).executeSQL(),哈哈,改动很小吧,而且一旦发现这种实现方式不可行,那么只要将ImportDataDecorate.executeSQL()的实现改成直接提交到数据库就好了,其他调用根本不用变。设计模式很伟大呀,只不过我都不知道我用的这是什么模式,Proxy 还是Decorator?不知道,反正解决问题了,呵呵。
posted @ 2006-03-07 22:08 CowNew开源团队 阅读(1029) | 评论 (7) | 编辑 收藏

1、人为了成功 总会失去些最本性的东西
要想得到一些东西必须要失去一些东西,只有失去一些东西才能得到一些东西,成功的人是得到的东西的价值大于失去的,失败的人是失去的东西的价值大于得到的东西
但是往往失去的是人性中最美好的东西  
美好的东西不等于有价值的东西,明白这个道理,你就成熟了,但是也就“俗”了 。那些美好的东西早晚有一天要失去的,你去尽力挽留他只会使你受的伤害更多
2、不知道从什么时候开始,再也不敢叫“男孩”“女孩”了,因为我们都已经是“人”了 。
3、每个人女孩都是天使,除去它们那身白色羽毛,你会发现她们不过是一群吸血蝙蝠
4、人有时流泪并不是多么爱着对方,而通常是自己感动自己,对曾经的付出的心血感到悲伤
5、以前我以为是个文学家,等我开始每天写blog以来我才发现,我TMD简直就是个文学家
posted @ 2006-03-06 23:00 CowNew开源团队 阅读(419) | 评论 (0) | 编辑 收藏

仅列出标题
共30页: First 上一页 22 23 24 25 26 27 28 29 30 下一页