<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>liwanchun_xd</title>
    <description></description>
    <link>http://liwanchun-xd.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>java.lang.OutOfMemoryError:PermGen space及其解决方法（转载）</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/209859" style="color:red;">http://liwanchun-xd.javaeye.com/blog/209859</a>&nbsp;
          发表时间: 2008年06月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          转载：　<a href="http://www.wujianrong.com/archives/2006/12/javalangoutofmemoryerror_permg.html" target="_blank">http://www.wujianrong.com/archives/2006/12/javalangoutofmemoryerror_permg.html</a><br /><br /><br />1、<br /><br />PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space从表面上看就是内存益出，解决方法也一定是加大内存。说说为什么会内存益出：这一部分用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域，它和和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理，所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。<br /><br />改正方法：-Xms256m -Xmx256m -XX:MaxNewSize=256m -XX:MaxPermSize=256m <br /><br />2、<br /><br />在tomcat中redeploy时出现outofmemory的错误.<br /><br />可以有以下几个方面的原因:<br /><br />１,使用了proxool,因为proxool内部包含了一个老版本的cglib.<br /><br />2, log4j,最好不用,只用common-logging<br /><br />3, 老版本的cglib,快点更新到最新版。<br /><br />４，更新到最新的hibernate3.2<br /><br />3、<br /><br />这里以tomcat环境为例，其它WEB服务器如jboss,weblogic等是同一个道理。<br />一、java.lang.OutOfMemoryError: PermGen space<br />PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,<br />这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,<br />它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对<br />PermGen space进行清理，所以如果你的应用中有很多CLASS的话,就很可能出现PermGen space错误,<br />这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小<br />超过了jvm默认的大小(4M)那么就会产生此错误信息了。<br />解决方法： 手动设置MaxPermSize大小<br /><br />修改TOMCAT_HOME/bin/catalina.sh<br />在&ldquo;echo "Using CATALINA_BASE:   $CATALINA_BASE"&rdquo;上面加入以下行：<br />JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m<br />建议：将相同的第三方jar文件移置到tomcat/shared/lib目录下，这样可以达到减少jar 文档重复占用内存的目的。<br /><br />二、java.lang.OutOfMemoryError: Java heap space<br />Heap size 设置<br />JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值，<br />其初始空间(即-Xms)是物理内存的1/64，最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可<br />进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。<br />提示：在JVM中如果98％的时间是用于GC且可用的Heap size 不足2％的时候将抛出此异常信息。<br />提示：Heap Size 最大不要超过可用物理内存的80％，一般的要将-Xms和-Xmx选项设置为相同，而-Xmn为1/4的-Xmx值。 <br />解决方法：手动设置Heap size<br />修改TOMCAT_HOME/bin/catalina.sh<br />在&ldquo;echo "Using CATALINA_BASE:   $CATALINA_BASE"&rdquo;上面加入以下行：<br />JAVA_OPTS="-server -Xms800m -Xmx800m   -XX:MaxNewSize=256m"<br /><br />三、实例，以下给出1G内存环境下java jvm 的参数设置参考：<br /><br />JAVA_OPTS="-server -Xms800m -Xmx800m  -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -Djava.awt.headless=true "<br /><br /><br />三、相关资料<br /><br /><a href="http://www.tot.name/show/3/7/20061112220131.htm" target="_blank">/show/3/7/20061112220131.htm</a><br /><br /><a href="http://www.tot.name/show/3/7/20061112220054.htm" target="_blank">/show/3/7/20061112220054.htm</a><br /><br /><a href="http://www.tot.name/show/3/7/20061112220201.htm" target="_blank">/show/3/7/20061112220201.htm</a><br /><br />题外话：经常看到网友抱怨tomcat的性能不如...，不稳定等，其实根据笔者几年的经验，从"互联星空&ldquo;到现在的房产门户网，我们<br />均使用tomcat作为WEB服务器，每天访问量百万多，tomcat仍然运行良好。建议大家有问题多从自己程序入手，多看看java的DOC文档<br />并详细了解JVM的知识。这样开发的程序才会健壮。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/209859#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 30 Jun 2008 17:41:54 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/209859</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/209859</guid>
      </item>
      <item>
        <title>axis2嵌入至webapp中的方案</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/198365" style="color:red;">http://liwanchun-xd.javaeye.com/blog/198365</a>&nbsp;
          发表时间: 2008年05月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          axis2嵌入至webapp中的方案（axis2版本号：1.4）<br /><br />1、<br />WEB-INF<br />    -- services<br />          -- BookService     (服务)<br />                -- META-INF<br />                       -- services.xml<br />2、web.xml<br />&lt;servlet><br />		&lt;servlet-name>AxisServlet&lt;/servlet-name><br />		&lt;servlet-class>org.apache.axis2.transport.http.AxisServlet&lt;/servlet-class><br />		&lt;load-on-startup>1&lt;/load-on-startup><br />	&lt;/servlet><br /><br />	&lt;servlet-mapping><br />		&lt;servlet-name>AxisServlet&lt;/servlet-name><br />		&lt;url-pattern>/services/*&lt;/url-pattern><br />	&lt;/servlet-mapping><br /><br /><br />3、demo:<br /><br />public class BookServiceHelper {<br />	private static BookServiceHelper helper = new  BookServiceHelper();<br />	private static final String NS_URI = "http://ws.b2b.com/imagedispose/services/BookService";<br />	<br />	public static void main(String[] args) {<br />		try{<br />                           /** 同步 */<br />			helper.testNewOperation();		<br />                           /** 异步 */<br />			helper.testStartNewOperation();<br />		}catch(Exception e){<br />			e.printStackTrace();<br />		}<br />	}<br />	/**<br />     * Auto generated test method<br />     */<br />    public void testNewOperation() throws java.lang.Exception {<br />        com.hc360.b2b.ws.bookservice.BookServiceStub stub = new com.hc360.b2b.ws.bookservice.BookServiceStub(NS_URI); <br />        BookServiceRequest param18 = (BookServiceRequest)getTestObject(BookServiceRequest.class);    <br />        param18.setName("sync");<br />        param18.setId(93);<br />              <br />        BookServiceResponse res = stub.SendMessage(param18);<br />        String msg  = res.getMsg();        <br />        System.out.println(msg + ", boolean: " + res.getValid());<br />    }<br /><br />    /**<br />     * Auto generated test method<br />     */<br />    public void testStartNewOperation() throws java.lang.Exception {<br />    	<br />        com.hc360.b2b.ws.bookservice.BookServiceStub stub = new com.hc360.b2b.ws.bookservice.BookServiceStub(NS_URI);<br />        BookServiceRequest param18 = (BookServiceRequest) getTestObject(BookServiceRequest.class);<br />        param18.setName("asyc");<br />        param18.setId(3);<br />      <br />        // todo Fill in the param18 here        <br />        BookServiceCallbackHandler call = new tempCallbackN65547();<br />        stub.startSendMessage(param18, call);<br />        <br />        int i = 0;<br />        while(!call.isComplete() && !call.isTimeOut()){<br />        	i++;<br />        	System.out.println("loop: " + i);<br />        	Thread.sleep(1000);<br />        }<br />        <br />        <br />        <br />        System.out.println("asynchronism！");<br />    }<br /><br />    //Create an ADBBean and provide it as the test object<br />    public org.apache.axis2.databinding.ADBBean getTestObject(<br />        java.lang.Class type) throws Exception {<br />        return (org.apache.axis2.databinding.ADBBean) type.newInstance();<br />    }<br /><br />    private class tempCallbackN65547<br />        extends com.hc360.b2b.ws.bookservice.BookServiceCallbackHandler {<br />        public tempCallbackN65547() {<br />            super(null);<br />        }<br /><br />        public void receiveResultSendMessage(<br />                BookServiceResponse param13) {<br />        	System.out.println("receiveResultNewOperation11");<br />        	System.out.println("call back result: " + param13.getMsg());<br />            System.out.println("receiveResultNewOperation22");<br />            this.setComplete(true);<br />        }<br /><br />        public void receiveErrorNewOperation(java.lang.Exception e) {<br />        	System.out.println("call back error!");<br />        }<br />    }<br />}
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/198365#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 29 May 2008 16:54:12 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/198365</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/198365</guid>
      </item>
      <item>
        <title>axis2-eclipse-codegen-wizard-1.4(2, 包含part5)</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/198356" style="color:red;">http://liwanchun-xd.javaeye.com/blog/198356</a>&nbsp;
          发表时间: 2008年05月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          官方axis2-eclipse-codegen-wizard-1.4版本在eclipse3.2+myeclipse5.0中不能使用，附件是我修改后的。<br /><br /><span style="color: red">上次忘记了提交part5, 现已提交。谢谢网友们的提醒！</span><span style="color: blue">不过，javaeye作的真烂，每个文件大小限制下2M以下，太不方便了。</span>
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/198356#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 29 May 2008 16:39:53 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/198356</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/198356</guid>
      </item>
      <item>
        <title>axis2-eclipse-codegen-wizard-1.4</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/198350" style="color:red;">http://liwanchun-xd.javaeye.com/blog/198350</a>&nbsp;
          发表时间: 2008年05月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          官方axis2-eclipse-codegen-wizard-1.4版本在eclipse3.2+myeclipse5.0中不能使用，附件是我修改后的。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/198350#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 29 May 2008 16:37:02 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/198350</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/198350</guid>
      </item>
      <item>
        <title>答复: 跨域的单点登录</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/189824" style="color:red;">http://liwanchun-xd.javaeye.com/blog/189824</a>&nbsp;
          发表时间: 2008年05月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我最近很仔细的深入了一下CAS,还给他们报了个bug,被接受了.<br />所以我想我有资格来解释一下CAS的东西吧.<br />CAS用来标志用户的token是存在它自己的域名下的,不是存在web应用的域名下的.CAS判断用户登录由agent决定,agent验证用户信息有两种处理.<br />1.如果这个用户是以前没登录过的,也就是说这个web应用的域名下没有一个agent颁发的sessionid(在cookie里)的话,那么它会跳转到CAS server的登录界面(这里由CAS server决定是否显示登录界面,还是直接就跳转回来,也就是不是每次都要求输入密码登录的).<br />2.CAS的登录界面会有两种处理.如果以前没登录过,也就是在CAS server自己的域名下,没有用户的token的话,就要求登录.如果有token的话,就返回一个ticket(也就是不显示登录界面直接重定向返回了).<br />然后接第一步里面,agent收到ticket以后就提交给CAS server验证,并取得返回信息(是个xml,cas的协议规定的,也可以用saml之类的,这个地方可以取得除了用户姓名以外的信息,这样就可以做同步了,权限的问题可以这么解决).然后agent就保存此时的session,把sessionid放到该web应用域名下的cookie里.<br />以上逻辑说明了,标志用户身份的token在CAS server的域名下,每个web应用不保存用户身份.因此跨域是绝对没问题的.只是single sign off就需要CAS server的配合了,要把那个token删掉.<br />不过如果标志身份的token不存在,而sessionid存在,那还是被认为是登录的,只不过是在等待那个session timeout而已.<br /><br />如果我的理解有误,希望大家指正.<br /><br />----------------------------------------------------------------<br />分析的很透彻！
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/189824#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 05 May 2008 17:01:28 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/189824</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/189824</guid>
      </item>
      <item>
        <title>Spring声明性事务之事务传播</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/182609" style="color:red;">http://liwanchun-xd.javaeye.com/blog/182609</a>&nbsp;
          发表时间: 2008年04月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Spring声明性事务：<br />class Test{<br />a(){<br />    doSomeThing();<br />    b();<br />}<br />b(){<br />}<br />}<br /><br />把事务切入点定义在a和b上，并且事务传播方式为requires_new.在实际运用中，b()方法中并不会新建事务。原因是不是对proxy的访问。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/182609#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 14 Apr 2008 16:07:41 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/182609</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/182609</guid>
      </item>
      <item>
        <title>Spring整合Hibernate之Session</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/181526" style="color:red;">http://liwanchun-xd.javaeye.com/blog/181526</a>&nbsp;
          发表时间: 2008年04月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          转载：http://bbs.tech.ccidnet.com/read.php?tid=557959<br /><br />Spring整合Hibernate时，主要做了两件事：提供事务级session和声明式的事务控制。 <br />在较早的Hibernate中，对于session的管理一般是one-session-per-operation的方式，即一次具体操作一个session。<br />Spring为了解决这个问题，引入了HibernateTemplate类。<br />先来看看它的文档中一段很有意思的话： <br />NOTE: As of Hibernate 3.0.1, transactional Hibernate access code can also be coded in plain Hibernate style. Hence, for newly started projects,consider adopting the standard Hibernate3 style of coding data access objects instead, based on SessionFactory.getCurrentSession().(Spring's LocalSessionFactoryBean automatically supports Spring transaction management for the Hibernate3 getCurrentSession() method.)作者说：在新开始的工程，可以考虑用标准的Hibernate3的编码方式作为HibernateTemplate的替代。因为Hibernate3提供的SessionFactory.getCurrentSession()已经取代了以往那种每次操作都open一个新Session的方式，同时Spring的LocalSessionFactoryBean自动支持Hibernate3的getCurrentSession()的事务管理。也就是说，如果不用HibernateTemplate这咱Spring的专有API，而只用Hibernate3,我们一样可以受用Spring的事务管理。<br />来详细地看看HibernateTemplate，因为它毕竟简化了Hibernate的操作，但是在有些情况下，我们应该使用Hibernate而不是用HibernateTemplate。根据HibernateTemplate的文档注释，它做了两件事：1.简化了Hibernate的数据访问编码;2.自动地将HibernateExceptions转化为Spring的异常体系中的DataAccessExceptions（这是一个unchecked exception）.<br />HibernateTemplate实现第一点，是通过回调来实现的，它的核心方法execute(): public Object execute(HibernateCallback action, boolean exposeNativeSession) m_),}<br />throws DataAccessException {<br />//这个Session是受Spring事务管理的<br />Session session = getSession(); <br />//这个是怎么回事，还要再仔细看看，我想它应该是关系到Session在这个操作里操作完是否关闭的关键 <br />boolean existingTransaction = SessionFactoryUtils<br />.isSessionTransactional(session, getSessionFactory()); FlushMode previousFlushMode = null;<br />try { <br />previousFlushMode = applyFlushMode(session, existingTransaction); <br />enableFilters(session); <br />//在默认情况下，不把Sessin暴露给用户<br />Session sessionToExpose = (exposeNativeSession ? session :<br />createSessionProxy (session)); <br />//exposeNativeSession默认值为false ; <br />//这里是真正涉及到Hibernate操作的地方<br />Object result = action.doInHibernate(sessionToExpose); <br />flushIfNecessary(session, existingTransaction); <br />return result; }<br />} <br />catch(...){ <br />//将Hibernate代码抛出的HibernateException，SQLException<br />//转化为 DataAccessExceptions，如果有运 行时异常，将其抛出 <br />} <br />} <br />finally { <br />if (existingTransaction) { <br />disableFilters(session); <br />if (previousFlushMode != null) { <br />session.setFlushMode(previousFlushMode); <br />} <br />} <br />else { <br />// Never use deferred close for an explicitly new Session.<br />if (isAlwaysUseNewSession()) { <br />//这里的默认值是false,所以此次操作结束后，session不会在此关闭<br />SessionFactoryUtils.closeSession(session);<br />} <br />else { <br />//没有硬性关闭Session,这是区别于Hibernate3以前版本的地方 SessionFactoryUtils.closeSessionOrRegisterDeferredClose<br />(session, getSessionFactory()); <br />} <br />} <br />} <br />} <br />真正的数据操作是在HibernateCallback action中实现的，为了执行action中的操作，需要一个Session,这个Session是在execute（）方法内部获得（不一定是新产生的）并传入的。另外，在操作执行完之后，这个Session没有硬性关闭，而是交由SessionFactoryUtils来决定是否立即关闭还是延迟关闭。有时间再看看SessionFactoryUtils .closeSessionOrRegisterDeferredClose（）具体做了些什么。<br />用HibernateTemplate比起直接用Hibernate编码简洁了很多。但是，作者在文档中写到：The major advantage is its <br />automatic conversion to DataAccessExceptions, the major disadvantage that no checked application exceptions can get <br />thrown from within data access code.因为封闭得太好了，我们根本无法干预HibernateTemplate的方法内部，因此我们<br />不能抛出检查型的应用异常。如果我们想在某个方法的内部在某个条件下抛出自定义的应用异常，就要用Hibernate直接编码了，这是不应该用HibernateTemplate的情况。<br />作者在文档中还写到：It can be used within a service implementation via direct instantiation(实例)with a SessionFactory reference, or get prepared in an application context and given to services as bean reference. 为了使用 <br />HibernateTemplate,我们需要一个SessionFactory,因为使用HibernateTemplate时需要获取Session，而Session是从SessionFactory获取的。我们可以在应用配置文件中，根据已配置的SessionFactory配置一个HibernateTemplate,或者在程序中要用时再根据已配置好的SessionFactory来产生一个HibernateTemplate。Spring提供了一个可配置的SessionFactory的工厂类，用以向容器暴露一个单例化的SessionFactory：LocalSessionFactoryBean。这个类的源码也很有意思，还要继续看一下。<br />为了进一步简化Hibernate的操作，Spring提供了一个用于DAO的基类HibernateDaoSupport：This base class is mainly<br />intended for HibernateTemplate usage。这个类只有唯一的成员变量private HibernateTemplate hibernateTemplate。但是我在想，Spring是不是做的太过分了？包装得太好了？？<br /><br />对于使用sessionFactory.getCurrentSession()方式下，会自动更改autocommit=false，建议以事务方式运行。而openSession(),autocommit则不会改动。<br />在Spring中SessionFactoryUtils.doGetSession()实现Session的获取。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/181526#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 11 Apr 2008 00:45:30 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/181526</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/181526</guid>
      </item>
      <item>
        <title>web service中的事务控制</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/181491" style="color:red;">http://liwanchun-xd.javaeye.com/blog/181491</a>&nbsp;
          发表时间: 2008年04月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          转载：http://www.griddss.cn/show.aspx?id=127&cid=7<br />    因为这个问题讨论起来内容比较多一些，所以另开一个话题。<br /> <br />    如果你只是要解决两个系统之间的事务同步问题，可以采用判断服务是否成功的办法来解决，即：<br />    <br />    * A系统开始自己的事务，处理自己的数据，然后。。。<br />    * 在提交之前调用B系统的服务。<br />    * B系统开始自己的事务B，在事务中处理数据，再提交事务。<br />    * B系统把自己事务的提交成功与否的信息做为返回值回馈A系统。<br />    * A系统根据B的事务成功情况决定自己的事务是否提交或是回滚。<br /> <br />    但是，在继续深入讨论这个问题之前，先反问一个引伸的问题：当分布式系统之间，要进行事务控制的子系统不是两个，而是N个时，如果进行事务控制？<br /> <br />    分布式事务一直都是很难解决的问题。在面向DCOM的分布式应用中，有一种分布带事务支持策略，大体的思路是采用两段式事务提交的办法，第一次提交是预提交，预提交之后是可以回滚的。第二次提交是永久性的提交，提交之后就不可以回滚。并且，如果预提交成功，第二次提交也必然成功，系统必须可以保证这一点。<br /> <br />    这样，当每个系统都支持这种两段式提交之后，就可以采用这样的事务管理：一个控制角色向每个分布系统提出执行要求，并要求完成第一次事务提交。当每个系统的第一次提交都成功时，则要求所有系统完成最后的永久提交，可知这次的永久提交是肯定可以完成的，因此不须要再担心这次提交是否成功。<br />    如果第一次提交中，有某些应用出现失败，则要求所有的应用都回滚事务。<br />    一些数据库软件本身就支持事务嵌套，如sqlserver等，不幸的是，我们的主力数据库informix不支持。<br />    为了简化这种分布式事务管理，有一些中间件产品可以采用，用得比较广泛的是MS DTS.<br /> <br />    你可能已经看出来了，这样的事务控制策略虽然可以在分布式环境下满足事务的ACID要求，但是它对各个分布组件是有要求的，在基于COM, remoting，JRMI一类技术的分布式应用程序中，这个没有问题。但是在采用web service的场景中，这是有问题的。<br /> <br />    问题1. web service是一种以松耦合为指导思想的集成方式，一般情况下，主张采用无状态方法。<br />    webservice主张两次调用之间没有上下文关系，即一次调用与其他之前和之后的调用都没有关系，一次提交即完成一次完整的处理。但是分布式事务却要求各方要在两次对话之间保持对话状态，以便于知道本次永久性提交时，要对之前“哪一个”已经被预提交成功的事务执行最后的提交。<br />    当遇到这个问题时，我们必须要再多问自己一个问题：我们已经选择了正确的集成技术吗？如果多个系统之间有如此紧密的事务耦合关系时，我很怀疑它们其实就是同一个应用系统。同一个应用系统中，应该有相同的平台，相同的进程空间，相同的数据模型以及数据源。这种情况下，采用web service是一种错误的选择，web service应该用于不同平台、不同应用、不同的数据模型的系统集成。即便是的确需要在同一个应用系统中由于某些原因而实现模块间的分布式构造，也应该采用同一技术平台内的远过程访问技术，它们能通常比web service能提供更好的耦合性支持。<br /> <br />    好吧，假设你经过思考之后，对上述问题的回答是“是”：我们确实必须要在异构的、多平台的、本来应该是低耦合系统之间实现分布式事务控制。那么，webservice还有用处吗？<br />    谢天谢地，web service虽然主张交互之间采无状态方式，但是它并不是禁止采用有状态的交互。WEB SERVICE还是一种web技术，而web技术中的状态保存可能是最早被解决的问题之一了。在所有的web开发技术平台中，都有session机制，无论这些Session是通过IP，cookies, hidden input来实现，还是url sessionid来实现的，反正都有办法实现，请参阅所用平台的session支持机制就可以了。退一万步，你也可以通过在服务器中维护一个应用程序级的事务池来实现，未最后提交的事务对象都放在里面，每一个事务对象都给定一个唯一个的标志ID来识别，形成一个字典对象池。如果启动事务成功，则把此事务的ID返回给调用者执有，做第二段提交时，把事务的ID做为参数提交就是了。（随便提一下，用这种方法时，千万不能把对象的指针、句柄、引用什么的平台相关的值交给客户方，倒不是害怕安全问题，而是这些值在分布系统中是没有意义的，上次返回的指针没准早被垃圾收集机挪到其他地方去了）<br /> <br />    无论如何，webservice在通信层上是一种无连接的协议，每两次调用之间，tcp连接是断开的，因此，一但采用session机制来管理上下文，你就必须为这些session的生命期负责。试想，如果一个事务上下文已经开启，而此时客户方系统却突然当机了，这时会出什么事情？在同一个应用程序域中，客户方的当机会让连接中断，服务器立即就会中断并回退事务，但是在webservice里，状态管理机无法立即感觉到此事务的调用方已经失去控制，只能在一定的时间之后，才发现：“噫？这个事务已经N长时间没有人访问了！快快回退！”在ASP.net里，默认的状态超时时长大概是20分钟，JSP也差不多，阻塞了20分钟的事务对数据源是什么影响可想而止！因此，必须考虑合适的状态时长与事务隔离级别，以减小对数据源的性能影响。       <br /> <br />    问题2. web service的“反模式”方法论使得无法在系统之间统一出共同的抽象接口。<br />    web service是一种“反模式”的系统架构思想，即不是一般的由先建模并抽象接口开始，再由各个分布系统实现接口的系统构造方式，而是反过来：系统可能早已经完成，现在的问题是两个系统间的信息交互作用，因此交互的接口规格是根据需要，把系统数据模型去范式化后挑挑捡捡而定的。<br />    因此，webservice中不支持接口抽象，即：你无法定义一个各个系统都必须实现的抽象事务接口，然后由各个系统实现这个接口的多态，最后在承担事务控制器的应用中调用统一事务接口以调度分布事务。虽然这样的接口模型在很多面向对象的开发平台中的远过程调用技术中所支持，但是如同之前说过的，web service是一种用于集成的松耦合的反模式方法论，而不是为紧耦合系统中的分布式对象而设计的。<br />    所以，虽然有点讨人烦，但是我又一次忍不住想问我已经问过的那个问题：我们真得用对了技术吗？如果多个系统之间需要如此级别的接口耦合性，我真得越来越怀疑它们其实就是同一个应用系统了。<br />    假设你的回答还是“是，他们真得不是同一个系统，他们是异构平台的，异构数据的！”好吧，那么继续。让我们采用web service来完成集成，但是你必须忘记你的OOP思想，老老实实地编码，用枯燥的、重复的代码把所有的系统的事务都控制在一起，别想用对象抽象的概念来省一点事。<br />    真的吗？<br />    如果把事务控制器独立出来如何？假设我们建立一个专用于分布式服务控制的应用，而用WEB SERVICE的方式公布接口, 允许其他应用程序通过向这个事务控制器注册自己的两段式事务开启、提交和回退的web service接口。然后，当有客户想启动分布事务时，就可以向这个事务控制器发起分布式事务请求，选择事务各方，启动一个分布式，最后向事务控制器，而非是各个事务方直接发起提交请求，这样事务控制的多态就可以在事务控制服务器中实现，虽然实现可能还是通过查表等方式实现，而非平台级的抽象方法，但是对于事务客户来讲，这样一个服务器就是多态的实现部分。<br />    如果真得比MS更快更好地实现这样一个web service做接口，面向异构系统的分布式事务控制器，NASDAQ也许会有你的一席之地吧！<br /> <br />    问题3. 异构平台不一定都支持两段事务提交模式。<br />    web service面向的是完全异构平台的集成，那么显然不能指望每个平台都能支持两段时提交事务模式。但是，标准就是标准，协议就是协议，标准就是用来让大家遵守的，如果一个平台本来不支持两段式事务，那么为了能支持分布式事务，它就必须改造以实现两段式事务提交。<br />    怎么改造是各个应用系统内部的事情，为了本文讨论的全整性，也在这里稍微涉及一下。<br />    首选的方式是通过数据缓存的方式来实现。很多OO系统中，都采用了所谓的N层架构，即把业务对象与关系表模型分离开来，业务对象位于系统内存或是缓存中，由运行时的对象容器管理，容器根据一定的策略，把缓存中业务对象向数据库这样的久永介质中保存，或是从数据库中加载所需要的业务对象，在保存和加载过程中，将完成对象到表数据的转换，或是相反。 <br /><br />    一般的N层结构的中间件产品中，都会提供两个级别的事务，即面向缓存中对象的事务控制和面向持久化过程的事务，可以考虑简单地将此两个事务级别对应的分布事务中的两段事务提交。但是，这种方式必须冒一定的风险，如对象容器级的事务成功，而数据库事务提交时出现失败，此时将会导致的数据不一致的风险，尽管这个几率并不很大。 <br /><br />    在使用数据容器的情况下，也可以用保存对象的历史状态来实现事务的手工回退。因为在业务对象层与持久化层相分离之后，持久化层在数据更新时并没有复杂的逻辑，只是一些被罗列的、业务意义无关的数据更新序列。如果可以保持对象的状态历史，那么就可以在需要的时候将对象的状态恢复到旧的旧版上。实际上，在一些出色的中间件平台中，这个机制已经实现得非常完善了。（可以参阅Graphtalk平台的对象持久化管理，简直是天才！） <br /><br />    另一种笨办法是通过数据逻辑来实现两段事务提交，例如在要求第一次提交时，即真正提交，在第二次提交时固定什么也不做，而返回正常。如果要求回退，那么就通过数据逻辑或是业务逻辑来更新数据为旧状态。这种实现方式绝对是很令人头痛的。 <br /><br />    不过，幸亏我们不是在为一个通用的数据库设计两段事务机制。要知道，面向服务的事务处理并不是如同数据库级别的事务那样，在事务的期间数据的操作有无穷的可能性。通常我们一个服务就是一个功能，其数据操作过程中，数据的变化方式是可预知的，因此恢复数据的状态也是一个个具体而固定的过程，只要我们针对每一个服务操作设计数据恢复机能就是了。 <br /><br />    最后，如果这些都不可能实现的话——大于50%的可能性，因为时间、成本、技术等原因，这些都实现不了，那么只能靠两个字了：妥协。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/181491#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 10 Apr 2008 22:46:58 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/181491</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/181491</guid>
      </item>
      <item>
        <title>XA分布式事务处理</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/181477" style="color:red;">http://liwanchun-xd.javaeye.com/blog/181477</a>&nbsp;
          发表时间: 2008年04月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          转载：http://www.blogjava.net/rednight/archive/2007/03/06/102070.html<br /><br />在谈到 XA 规范之前，必须首先了解分布式事务处理（ Distributed Transaction Processing ， DTP ）的概念。 Transaction ，即事务，又称之为交易，指一个程序或程序段，在一个或多个资源如 数据库 或文件上为完成某些功能的执行过程的集合。 LU 人的博客 )<br />　　分布式事务处理是指一个事务可能涉及多个数据库操作，分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作，提交或回滚事务的决定必须产生统一的结果（全部提交或全部回滚）。 <br />　 X/Open 组织（即现在的 Open Group ）定义了分布式事务处理模型。 X/Open DTP 模型（ 1994 ）包括应用程序（ AP ）、事务管理器（ TM ）、资源管理器（ RM ）、通信资源管理器（ CRM ）四部分。一般，常见的事务管理器（ TM ）是交易中间件，常见的资源管理器（ RM ）是数据库，常见的通信资源管理器（ CRM ）是消息中间件。<br />　　通常把一个数据库内部的事务处理，如对多个表的操作，作为本地事务看待。数据库的事务处理对象是本地事务，而分布式事务处理的对象是全局事务。 <br /><br />    所谓全局事务，是指分布式事务处理环境中，多个数据库可能需要共同完成一个工作，这个工作即是一个全局事务，例如，一个事务中可能更新几个不同的数据库。对数据库的操作发生在系统的各处但必须全部被提交或回滚。此时一个数据库对自己内部所做操作的提交不仅依赖本身操作是否成功，还要依赖与全局事务相关的其它数据库的操作是否成功，如果任一数据库的任一操作失败，则参与此事务的所有数据库所做的所有操作都必须回滚。 <br />　　一般情况下，某一数据库无法知道其它数据库在做什么，因此，在一个 DTP 环境中，交易中间件是必需的，由它通知和协调相关数据库的提交或回滚。而一个数据库只将其自己所做的操作（可恢复）影射到全局事务中。 <br />　　 XA 就是 X/Open DTP 定义的交易中间件与数据库之间的接口规范（即接口函数），交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。 XA 接口函数由数据库厂商提供。 <br /><br />XA 与两阶段提交协议 <br />　　通常情况下，交易中间件与数据库通过 XA 接口规范，使用两阶段提交来完成一个全局事务， XA 规范的基础是两阶段提交协议。<br />　　在第一阶段，交易中间件请求所有相关数据库准备提交（预提交）各自的事务分支，以确认是否所有相关数据库都可以提交各自的事务分支。当某一数据库收到预提交后，如果可以提交属于自己的事务分支，则将自己在该事务分支中所做的操作固定记录下来，并给交易中间件一个同意提交的应答，此时数据库将不能再在该事务分支中加入任何操作，但此时数据库并没有真正提交该事务，数据库对共享资源的操作还未释放（处于上锁状态）。如果由于某种原因数据库无法提交属于自己的事务分支，它将回滚自己的所有操作，释放对共享资源上的锁，并返回给交易中间件失败应答。 在第二阶段，交易中间件审查所有数据库返回的预提交结果，如所有数据库都可以提交，交易中间件将要求所有数据库做正式提交，这样该全局事务被提交。而如果有任一数据库预提交返回失败，交易中间件将要求所有其它数据库回滚其操作，这样该全局事务被回滚。 <br />　　以一个全局事务为例， AP 首先通知交易中间件开始一个全局事务，交易中间件通过 XA 接口函数通知数据库开始事务，然后 AP 可以对数据库管理的资源进行操作，数据库系统记录事务对本地资源的所有操作。操作完成后交易中间件通过 XA 接口函数通知数据库操作完成。交易中间件负责记录 AP 操作过哪些数据库（事务分支）。 AP 根据情况通知交易中间件提交该全局事务，交易中间件会通过 XA 接口函数要求各个数据库做预提交，所有数据库返回成功后要求各个数据库做正式提交，此时一笔全局事务结束。 <br />　　 XA 规范对应用来说，最大好处在于事务的完整性由交易中间件和数据库通过 XA 接口控制， AP 只需要关注与数据库的应用逻辑的处理，而无需过多关心事务的完整性，应用设计开发会简化很多。 <br />　　具体来说，如果没有交易中间件，应用系统需要在程序内部直接通知数据库开始、结束和提交事务，当出现异常情况时必须由专门的程序对数据库进行反向操作才能完成回滚。如果是有很多事务分支的全局事务，回滚时情况将变得异常复杂。而使用 XA 接口，则全局事务的提交是由交易中间件控制，应用程序只需通知交易中间件提交或回滚事务，就可以控制整个事务（可能涉及多个异地的数据库）的全部提交或回滚，应用程序完全不用考虑冲正逻辑。 <br />　　在一个涉及多个数据库的全局事务中，为保证全局事务的完整性，由交易中间件控制数据库做两阶段提交是必要的。但典型的两阶段提交，对数据库来说事务从开始到结束（提交或回滚）时间相对较长，在事务处理期间数据库使用的资源（如逻辑日志、各种锁），直到事务结束时才会释放。因此，使用典型的两阶段提交相对来说会占用更多的资源，在网络条件不是很好，如低速网、网络颠簸频繁，情况会更为严重。 <br />　　当一个全局事务只涉及一个数据库时，有一种优化方式，即一阶段提交。当 AP 通知交易中间件提交事务时，交易中间件直接要求数据库提交事务，省去两阶段提交中的第一阶段，可以缩短处理一个事务的时间，以提高事务处理的效率。作为两阶段提交的一种特例，与两阶段一样，一阶段提交也是标准的。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/181477#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 10 Apr 2008 22:20:39 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/181477</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/181477</guid>
      </item>
      <item>
        <title>使用HSQLDB</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/180249" style="color:red;">http://liwanchun-xd.javaeye.com/blog/180249</a>&nbsp;
          发表时间: 2008年04月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          使用HSQLDB:<br />    1、下载HSQLDB的lib包至本地。<br />     2、使用 java -classpath ../lib/hsqldb-1.8.0.7.jar org.hsqldb.Server启动HSQLDB服务。<br /><br /><br />在使用 Serializable pojo = (Serializable)session.load(xx.class, id);时，在eclipse的变量窗口中，看到对象的属性值都为null,可以使用pojo.getXX()却能看见值，疑惑......
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/180249#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 07 Apr 2008 18:50:16 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/180249</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/180249</guid>
      </item>
      <item>
        <title>互联网之开放API</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/178083" style="color:red;">http://liwanchun-xd.javaeye.com/blog/178083</a>&nbsp;
          发表时间: 2008年03月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          转载：http://netgodfather.blog.sohu.com/74926448.html<br /><br />Facebook 成功了，而且不是小成。<br /><br />当初 Facebook 以校园为基础，掀起真人社交网络的时候，就引来了大量的跟风者。这个跟风还不是中国人民的专利，是全世界都在跟风。记得前段时间就有人列出了各种语言的Facebook 跟风网站。<br /><br />后来，Facebook 开始开放了API，引来了大量的第三方应用在Facebook上驻扎。这甚至引起了Google这个互联网老“新秀”的警惕。开放API似乎也会开始逐步成为一种风潮。<br /><br />但是，谁想做平台？谁能做平台？谁想做内容？谁能做内容？<br /><br />Facebook的这种开放API，模式很简单，我出用户，你出内容。一起给用户服务。我不动你的内容，你别想动我的用户。有钱大家一起赚。<br /><br />当然了，Facebook 作为平台，就像赌场里坐庄的一样。是有先天优势的。而这个先天优势，当然也是基于他前期的庞大用户基础，以及用户已经对这个平台有足够的粘性而建立起来的。<br /><br />Facebook的这个模式，一眼都看的出来，作为平台的 Facebook 牢牢的占据了产业的上游。而内容服务商，是处于产业的中下游，在各种方面，都受制于Facebook。就像中国移动一跺脚，SP就死一片一样。哪天Facebook打个喷嚏，这些 Facebook 的内容提供商也都得感冒不可。<br /><br />谁不想占据产业的上游呢？很显然，把自己做成平台，成为了不少“类Facebook”网站的目标。<br /><br />不过，平台可不是怎么好建的，平台首先就必须有牢固的用户基础和对用户足够的吸引力，否则，一开放，用户都跟别人跑了，不成了“赔了夫人，又折兵”了。<br /><br />曾经在海内问过王兴，他的意思，海内未来也一定会走Facebook的路子，开放API。另外，也有人说校内也要开放API了。<br /><br />另外，现在在WEB2.0领域，“老当益壮”的搜狐，也开始发力了。搜狐的博客搞了一个SOW，好像是有想做平台的意思。但是好像博客这个东西，也不是特别是和做平台，而且从现在看，开放的还不够。仅仅是一个 input 而已。<br /><br />做不了平台，咱们就做内容吧。做内容，首先就要有内容，而且还有卖点，有技术。并且要有除了用户资源之外的“核心竞争力”。否则，有了内容，也很可能给别人做嫁衣。帮别人探路。<br /><br />前不久，豆瓣也开放了API，没有深入研究，应该就是走内容服务商的样子吧。不过，豆瓣最核心的竞争力在什么地方？不是图书资料，甚至不是豆瓣拥有多么强大的技术优势。而是在用户，是在豆瓣拥有一大群很多网站所不拥有的“大量的、优质的用户群”。作为这样的网站，他适合做一个平台的内容服务商吗？或者说，他会心甘情愿的去站到一个产业的下游来觅食呢？…… <br /><br />开放API，是必然的趋势。但是，这个产业链的建立和健全，任重道远。尤其是在中国——这个对创意、对知识产权相对漠视的国度。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/178083#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 31 Mar 2008 15:53:12 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/178083</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/178083</guid>
      </item>
      <item>
        <title>试题集之一</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/178068" style="color:red;">http://liwanchun-xd.javaeye.com/blog/178068</a>&nbsp;
          发表时间: 2008年03月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          一、 一个文本文件有多行，每行为一个URL。请编写代码，统计出URL中的文件名及出现次数。<br /><br />　　a) 文件名不包括域名、路径和URL参数，例如http://www.ourday.cn/bbs/forumdisplay.php?fid=18中的文件名是forumdisplay。<br /><br />　　b) 部分URL可能没有文件名，例如http://www.ourday.cn/，这类统计为“空文件名”。<br /><br />　　c) 出现在不同URL中的相同文件名视为同一文件名，例如http://www.ourday.cn/index.php和ftp://ftp.ourday.cn/index.php为同一文件名<br /><br />　　文件内容示例如下：<br /><br />　　http://www.ourday.cn/bbs/redirect.php?tid=480&goto=lastpost#lastpost<br /><br />　　http://www.ourday.cn/index.php<br /><br />　　ftp://ftp.ourday.cn/index.php<br /><br />　　http://www.ourday.cn/bbs/index.php?k=8<br /><br />　　http://www.ourday.cn/bbs/forumdisplay.php?fid=16<br /><br />　　http://www.ourday.cn/bbs/viewthread.php?tid=444&extra=page%3D1<br /><br />　　http://www.ourday.cn/<br /><br />      http://www.ourday.com.cn/<br /><br />以上url地址经修改，不影响试题内容。<br /><br />　　二、 一个简单的论坛系统，以数据库储存如下数据： <br /><br />　　用户名，email，主页，电话，联系地址，发帖标题，发帖内容，回复标题，回复内容。<br /><br />　　每天论坛访问量300万左右，更新帖子10万左右。<br /><br />　　请给出数据库表结构设计，并结合范式简要说明设计思路。<br /><br />　　三、 现有两个文件， <br /><br />　　a)数据文件A，格式为：关键词、IP地址、时间，记录条数为1000万左右，该文件是无序排列的。<br /><br />　　b)数据文件B是关键词ID到关键词的对应表文件，格式为：ID、关键词，记录条数在100万左右，也是无序排列的。该对应表中的记录是一一对应的，不存在ID或者关键词重复的情况。<br /><br />　　要求将数据文件A对应的关键词替换为B中的ID，生成新的数据文件C，数据文件C的格式为：关键词ID、IP地址、时间。<br /><br />　　请设计一个程序，实现上述功能，并分析时间复杂度和空间复杂度。运行程序所使用的服务器的内存为1G，硬盘足够大。(至少要给出关键算法和设计思路)<br /><br /><br /><br />以下是该题的java实现，代码如下：<br /><br />import java.net.*;<br />import java.io.*;<br />import java.util.*; <br /><br />/** * @author tzy * 在j2sdk1.4.2下测试通过 */<br /><br />public class FileNameStat{<br />  private String srcPath;//要统计的文件路径<br />  private Map statMap;//用于统计的map<br />  <br />  public FileNameStat(String srcPath)<br />  {<br />     this.srcPath=srcPath;<br />     statMap=new TreeMap();<br />  }<br />  <br />  /*获得要统计的URL的文件名*/<br />  public String getFileName(String urlString)<br />  {<br />     URL url=null;<br />     String filePath=null;<br />     String fileName=null;<br />     try<br />     {<br />       url=new URL(urlString);<br />       filePath=url.getPath();<br />       int index=0;<br />       if ((index=filePath.lastIndexOf(\"/\"))!=-1)<br />       {<br />       fileName=filePath.substring(index+1);<br />       }<br />       else<br />       {<br />         fileName=\"\";<br />       }<br />     }<br />     catch(MalformedURLException e)<br />     {<br />     }<br />     return fileName;<br />  }<br />  <br />  /*统计指定文件名的个数*/<br />  public void stat(String filename)<br />  {<br />     Integer count=null;<br />     if(statMap.get(filename)!=null)<br />     {<br />       count=(Integer)statMap.get(filename);<br />       count=new Integer(count.intValue()+1);<br />     }<br />     else<br />     {<br />       count=new Integer(1);<br />     }<br />     statMap.put(filename,count);<br />  }<br />  <br />  /*统计的主方法*/<br />  public void start() throws FileNotFoundException,IOException<br />  {<br />     BufferedReader bfin=new BufferedReader(new FileReader(this.srcPath));<br />     String temp=null;<br />     while((temp=bfin.readLine())!=null)<br />     {<br />       stat(getFileName(temp));<br />     }<br />  }<br />  <br />  /*输出统计结果*/<br />  public void result()<br />  {<br />     Iterator it=statMap.entrySet().iterator();<br />     while(it.hasNext())<br />     {<br />       Map.Entry entry=(Map.Entry)(it.next());<br />       System.out.println((entry.getKey().equals(\"\")?\"空文件名\":entry.getKey()) + \"的个数是\" + entry.getValue());<br />     }<br />  }<br />  <br />  public static void main(String[] args) throws Exception<br />  {<br />     FileNameStat fns=new FileNameStat(\"src.txt\");//指定成待统计文件<br />     fns.start();<br />     fns.result();<br />  }<br />}<br /><br /><br />　　第二题<br /><br />　　简评：<br /><br />　　这道题也与百度的业务有关，百度现在除了搜索外，还有贴吧，知道，博客等重要产品。　　同时也在积极的探索社区化，包括前不久宣布进军电子商务领域，搜索之外的这些产品，其主要功能的实现主要是对数据库的操作。　　因此，想进入百度，也需要对数据库有一定的认识。 　　实现思路及数据库设计：　　1，该论坛主要有两个实体对象，用户和帖子;对于帖子对象，有一个问题：回复的帖子是否应该跟主题帖子存放在同一个表里?<br /><br />　　考虑到每天更新10万帖子，说明帖子数比较多，为了方便主题的呈现，我一般都把主题贴和回帖分别放在不同的表中，把主题贴和回帖分开可以提高查询效率(300万的访问量每天)。<br /><br />　　2，按照1中的思路，该论坛由两个对象(用户和帖子)变成三个实体对象，分别是用户，主题帖子，回复帖子;<br /><br />　　3，上述三个对象存在三个关系，分别是：<br /><br />　　用户--主题帖，一个用户可以发0个或多个帖子，一个帖子对应一个用户(一对多关系)，<br /><br />　　主题帖--回复帖：一个主题有0个或多个回复帖子，一个回复帖子对应一个主题(一对多关系);<br /><br />　　用户--回复贴：一个用户可以回0个或多个帖，一个帖子对应一个用户(一对多关系)。<br /><br />　　还存在对回复贴的回复，这个考虑用fatherId来表示。<br /><br />　　4，由于三个关系 “用户--主题帖，主题帖--回复帖，用户--回复贴” 都是一对多关系，根据表设计一般原则，可以将这两个关系独立建立表，也可以不另外建表而将一对多的关系体现在实体表中;然而，表间的连接查询是非常耗资源的，所以应尽量减少表间连接，那么对三个关系不应该分别建表，而是把用户的id作为主题表和回帖表的外键，把主题贴id作为回帖表的外键。<br /><br />　　5，鉴于以上考虑，该论坛的三个表如下所示<br /><br />　　表名：t_user_info (用户信息表)<br /><br />字段名   类型  缺省值  中文含义  约束   备注  <br />id   Int    用户编号   PRI  Auto_increment <br />Name   Varchar(30)     用户名     <br />Email   Varchar(50)         <br />Phone   Varchar(30)           <br />Addr  Varchar(200)         <br /><br /><br />　　其他字段略，根据需要添加　　表名：main_content_info (主题帖信息表)<br /><br />字段名  类型   缺省值  中文含义  约束  备注 <br />id  Int    贴编号  PRI  Auto_increment <br />Title  Varchar(200)    发帖标题     <br />Content  Text    发帖内容     <br />UserID   Int     用户编号    外键 <br /><br /><br />　　其他字段略，根据需要添加<br /><br /><br />　　表名：sub_content_info (回复贴信息表)<br /><br />字段名 类型   缺省值  中文含义 约束  备注  <br />id   Int    贴编号  PRI  Auto_increment <br />Title  Varchar(200)    发帖标题     <br />Content   Text    发帖内容     <br />UserID  Int    用户编号    外键 <br />FatherID  Int    父编号     <br />MainID  Int    主题帖编号    外键 <br /><br /><br />　　其他字段略，根据需要添加<br /><br />　　6，符合范式分析：<br /><br />　　上述表中每个字段不可再分，首先满足1NF;<br /><br />　　然后数据库表中的每个实例或行都是可以被惟一地区分(id)，不存在部分依赖，因此满足2NF;<br /><br />　　t_user_info (用户信息表)和main_content_info (主题帖信息表)不存在任何传递依赖，至少属于BCNF;<br /><br />　　但是sub_content_info (回复贴信息表)不满足3NF,因为存在如下传递依赖：id-->FatherID,FatherID-->MainID。<br /><br />　　范式并不是越高越好，sub_content_info表只满足2NF却更有效率，也是当今论坛较主流的设计。<br /><br />　　第三题<br /><br />　　简评：<br /><br />　　如何对海量数据进行快速检索，这是搜索引擎的必需考虑的问题。这又涉及到数据结构和算法。　　因此，要想进入百度，就必须熟悉一些基本的算法和数据结构。　　　思路及解决方案如下：<br /><br />　　1: 设计用TRIE树实现关键词到其对应id的快速词典查找<br /><br />　　TRIE树的每一个节点为一个包含256个元素的数组，同时指针指向其下一级节点<br /><br />　　节点定义如下：<br /><br />struct trienode<br />{<br />  int   id;<br />  struct trienode *child[256];<br />}TRIENODE; <br /><br /><br />　　如果TRIE树的某个节点的指针为NULL，说明从跟节点到当前节点的路径构成文件B中的一个关键词，<br /><br />　　在其节点的id保存该关键词的id;如果指针不为NULL，则id对应为0或者一个无穷大的整数，标志从根节点<br /><br />　　到当前节点的路径不是一个完整的关键词。<br /><br />　　将关键词转化为二进制无符号char型数组，即对于汉字等双字节字符视为两个无符号char型整数，<br /><br />　　每个元素的取值范围在0到255之间。<br /><br /><br /><br />　　2：生成文件b的TRIE树<br /><br />　　步骤1：依次读取文件b的每一行，对每一行执行步骤2到步骤5<br /><br />　　步骤2：读取关键词id和关键词，令为key<br /><br />　　步骤3：依次读取key的每一个字符，对每一个字符，执行步骤4;<br /><br />　　步骤4：如果该字符对应的指针为NULL，则创建其儿子节点;<br /><br />　　步骤5：为当前节点的对应字符id置为关键词id<br /><br />　　3：根据A文件生成C文件<br /><br />　　步骤1：依次读取文件A的每一行，对每一行执行步骤2到步骤5<br /><br />　　步骤2：分别获取当前行关键词、ip地址和时间<br /><br />　　步骤3：令关键词key=c1c2...cm，对c1到cm每个字符，执行步骤4<br /><br />　　步骤4：获取根节点的第c1个元素指针，转移到节点node1，<br /><br />　　根据node1的第c2个元素指针，转移到node2...<br /><br />　　根据nodem的第cm个元素，获取关键词的id<br /><br />　　步骤5：往文件c中写入一行数据，格式为关键词的id、ip地址和时间<br /><br />　　4：复杂度分析<br /><br />　　生成文件B的TRIE树过程时间复杂度为O(n*m)，其中n为文件b行数，m为文件b关键词的最大长度。TRIE的空间复杂度为O(n*m)，n和m含义同上，但由于实际应用中关键词之间可能会有很多前缀相同现象，所以实际耗费空间并不会很高。<br /><br />　　生成C文件的时间复杂度同样为O(n*m)，n为文件a行数，m为文件a关键词的最大长度，因为有了TRIE树之后，给定一个关键词获得其id的时间复杂度为关键词长度。生成C文件的过程除了TRIE树空间外基本不需要太多额外的空间，空间复杂度为O(1)，由于系统有1G的可用内存，TRIE占用的空间在几十兆到200M之间(与关键词集合有关)，因此本方法完全可行。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/178068#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 31 Mar 2008 15:22:12 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/178068</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/178068</guid>
      </item>
      <item>
        <title>多线程读写同步</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/178009" style="color:red;">http://liwanchun-xd.javaeye.com/blog/178009</a>&nbsp;
          发表时间: 2008年03月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          写模型 <br /><br />读写模型是一个稍微复杂一些的模型。 <br />一份共享资源允许多个读者同时读取。但是只要有一个写者在写这份共享资源，任何其他的读者和写者都不能访问这份共享资源。 <br />读写模型实现起来，不仅需要信号量机制，还需要额外的读者计数和写者计数。 <br />public static final Object signal = new Object(); <br />public static int readers = 0; <br />public static int writers = 0; <br /><br />// 读者代码 <br />… read() { <br /><br />for(… ) { // 循环执行 <br /><br />synchronized(signal){ <br />while( writers > 0 ) <br />signal.wait(); // 如果有人在写，那么就放弃执行，进入待召队列 <br /><br />// 能够到达这里，说明没有人在写 <br /><br />readers ++ ; // 增加一个读者计数，表示本线程在读取 <br />} // 这里出了synchronized范围,释放同步锁.以便其他线程读取. <br /><br /><br />// 进行一些读取操作 <br /><br />synchronized(signal){ <br />readers --; // 读取完成，减少一个读者计数，表示本线程不在读取 <br /><br />signal.notifyAll(); // 通知待召队列里面的所有其他线程 <br />} <br />} <br />} <br /><br />// 写者代码 <br />… write() { <br /><br />for(… ) { // 循环执行 <br /><br />synchronized(signal){ <br />while( writers > 0 || readers > 0) <br />signal.wait();// 如果有人在写或读，那么就放弃执行，进入待召队列 <br /><br />// 能够到达这里，说明没有人在写，也没有人在读 <br /><br />writers ++ ; // 增加一个写者计数，表示本线程在写 <br /><br />// 进行一些读取操作 <br /><br />writers --; // 读取完成，减少一个读者计数，表示本线程不在写 <br /><br />signal.notifyAll(); // 通知待召队列里面的所有其他线程 <br />} <br />} <br />} <br /><br />上述代码只是一段示意代码。实际应用中，人们通常抽取出来一个专门的读写同步锁。 <br />interface ReadWriteLock { <br />… getReadLock(); <br />… releaseReadLock(); <br />… getWriteLock(); <br />… releaseWriteLock(); <br />} <br /><br />具体的实现原理也是类似的信号量同步机制。 <br />class RWLock { <br />… readers, writers; <br /><br />… synchronized … getReadLock() { // 相当于synchronized(this) <br />… <br />while( writers > 0 ) <br />this.wait(); // 这里我们把RWLock对象本身作为信号量 <br />readers++; <br />} <br /><br />…synchronized … releaseReadLock(){ //相当于synchronized(this) <br />readers--; <br />this.notifyAll(); // // 这里我们把RWLock对象本身作为信号量 <br />} <br /><br />…synchronized … getWriteLock(){// 相当于synchronized(this) <br />while( writers > 0 || readers > 0 ) <br />this.wait(); // 这里我们把RWLock对象本身作为信号量 <br /><br />writers++; <br />} <br />…synchronized … releaseWriteLock(){// 相当于synchronized(this) <br />writers--; <br />this.notifyAll(); // // 这里我们把RWLock对象本身作为信号量 <br />} <br />} <br /><br />具体用法是 <br />public static final RWLock lock = new RWLock(); <br /><br />… read() { <br />lock.getReadLock(); <br />// 读取 <br />lock.releaseReadLock(); <br />} <br /><br />… write() { <br />lock.getWriteLock(); <br />// 读取 <br />lock.releaseWriteLock(); <br />} <br /><br />这种用法要求在执行一些处理之前，一定要执行某项特殊操作，处理之后一定也要执行某项特殊操作。这种人为的顺序性，无疑增加了代码的耦合度，降低了代码的独立性。很有可能会成为线程死锁和资源操作冲突的根源。 <br />这点一直让我不安，可是没有找到方法避免。毕竟，死锁或者资源操作冲突，是线程的固有问题。 <br />很巧的是，正在我惴惴不安的时候，我的一个朋友提供了一个信息。Sun公司根据JCR，决定在jdk1.5中引入关于concurrency（并发）的部分。 <br />以下这个网址是concurrency部分的util.concurrent一个实现。非常好的信息。对于处理多线程并发问题，很有帮助。 <br />http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html <br />里面提供了一个ReadWriteLock类，标准用法如下。 <br />Standard usage of ReadWriteLock: <br />class X { <br />ReadWriteLock rw; <br />// ... <br />public void read() throws InterruptedException { <br />rw.readLock().acquire(); <br />try { <br />// ... do the read <br />} <br />finally { <br />rw.readlock().release() <br />} <br />} <br />public void write() throws InterruptedException { <br />rw.writeLock().acquire(); <br />try { <br />// ... do the write <br />} <br />finally { <br />rw.writelock().release() <br />} <br />} <br />} <br />我们可以看到，ReadWriteLock同样要求调用的顺序——aquire()和release()。我对自己的例子增强了一点信心。 <br />我又查看了WriterPreferenceReadWriteLock类，看到里面成对的方法，startRead()，endRead()；startWrite()，endWrite()。我的心情完全放松了下来。我的思路虽然粗糙，但大体的方向是正确的。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/178009#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 31 Mar 2008 13:15:11 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/178009</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/178009</guid>
      </item>
      <item>
        <title>Facebook 平台详解 </title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/177929" style="color:red;">http://liwanchun-xd.javaeye.com/blog/177929</a>&nbsp;
          发表时间: 2008年03月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          转载：http://xiecc.blog.163.com/blog/static/14032200791635949331/<br /><br />全球第二大社交网站的facebook推出的开发平台在几个月之内迅速走红。在拒绝了 yahoo，google等的收购后，它的狼子野心也暴露无遗，它要做基于web的OS，在它的开放发台上可以搭建集成任何应用。游戏、工作、理财一切都在facebook中了，浏览器+facebook，会成为以后人们的生活方式吗？<br /><br />然而不得不说facebook的官方文档既乱且差， 很多刚开发facebook应用的人可能都会丈二和尚摸不着头脑。这篇日志算是八卦+tutorial吧。<br /><br /> <br /><br />一、facebook简介<br /><br />这段纯属废话，给那些不解facebook， 又不愿意点链接的人看。<br /><br />Facebook发源于哈佛大学，是目前社会化网络和web2.0的风向标。这个网站目前全球排名第8位，估值可能超过100亿美元。而Facebook开放平台的推出，更是让互联网业内认为它是最有可能和Google比肩的公司。 Facebook创建于2004年2月。这样的高速增长和短短三年多取得的成就，成为当今互联网发展的一个奇迹。<br /><br /> <br /><br />（Facebook创始人兼CEO Mark Zuckerberg）<br /><br /> <br /><br />这里的介绍更详细：<br /><br />http://www.yeeyan.com/articles/view/thunder/2346<br /><br /> <br /><br />二、facebook platform<br /><br />2007年5月24日，Facebook推出应用编程接口（API）。通过这个API，第三方软件开发者可以开发在Facebook网站运行的应用程序。这被称为Facebook开放平台（Facebook Platform）。<br /><br />没有什么比facebook创始人mark Zackerberg的总结更好了：<br /><br />"We want to make Facebook into something of an operating system so you can run full applications," Zuckerberg told me, saying it would be analogous to the platform that Microsoft Windows provides for developers.<br /><br />去 http://www.facebook.com/apps/ 看看吧， 那里的应用真是应用尽有。<br /><br />读写网总结的top 10 facebook apps也相当的棒：<br /><br />http://www.readwriteweb.com/archives/top_10_facebook_apps_work.php<br /><br />http://www.readwriteweb.com/archives/top_10_facebook_apps_play.php<br /><br /> <br /><br />三、facebook 应用概述<br /><br />1、平台开发环境<br /><br />Facebook的开发环境是LAMP， 这套传统的linux+apache+mysql+php的架构尽管被很多java程序员和ruby程序员所不屑，但它却仍然以绝对的优势占据着主导地位。<br /><br />不过这对java程序员来说确实有点痛苦，因为facebook官方包装的java client相当的差，更关键的是它没有提供任何java开发web应用的例子和文档。幸亏还有一些非官方的tutorial。地址附在后面。<br /><br /> <br /><br />2、应用集成<br /><br />谈到应用集成， 我们首先想到的是web services 和SOA，这些被工业界吹了那么多年的buzzword终于得到了推广，然而值得讽刺的是最后web services的推广形式不是他们花了那么多年想出来的SOAP标准，而是最简单又不用任何标准的REST，facebook正是提供了一堆REST的 Web services（从严格意义上说facebook的所有service都是POST过去的，URL也没有完全遵守REST）。<br /><br />然而这个层面的集成显然不能满足facebook作为web OS的需要，facebook需要让application运行在它提供的平台上。看看操作系统的需要就能想象到facebook的web OS应用提供怎么的集成。<br /><br />在windows上我们需要安装应用软件，facebook提供了完整的搜索、浏览、添加application的方式。<br /><br />在windows上我们利用各种快捷方式让应用运行在自己的平台里，facebook提供了运行应用的简单入口，而所有的应用都是在facebook内部展示的。<br /><br />记得前几天看到的一篇文章将应用集成分为三个层面：<br /><br />1 基于web services和SOA的应用程序交互<br /><br />2 平台运行在内部服务器上，而各种应用运行在外部服务器上，这正是facebook的方式<br /><br />3 平台和应用都运行在内部服务器上<br /><br />但是不知道他有没有想过第三种集成方式的扩展性和伸缩性是多么贫乏，我相信facebook的集成方式才是最好的方式。<br /><br />Facebook的这篇官方文档解剖了facebook应用集成到平台后的各个界面展示：<br /><br />http://developers.facebook.com/anatomy.php<br /><br /> <br /><br />四、facebook应用种类<br /><br />Facebook提供了三类的应用：<br /><br />External application<br /><br />Iframe          <br /><br />fbml<br /><br />1、 external application没什么好说的，就是基于web services的集成，外部应用在经过facebook的认证后可以调用facebook提供的一些web services。<br /><br />2、 Iframe的集成也相当简单，只是在facebook平台的应用页面上放了个iframe， iframe里跑的是应用程序的应用。好象facebook里的最火的应用之一top friends是用iframe做的。但是由于iframe的天生限制，使它无法完全集成facebook平台提供的功能，如fbml，在页面的显示上也有些怪。<br /><br />3、目前应用用的最多还是fbml。因为在fbml的应用中，facebook平台的页面（下图所示的菜单部分）和应用程序的页面（如下图黄色部分）是无缝显示的，看上去完全象是一个应用做的事。想象成OS的话，外部的菜单栏更象是windows提供的桌面和开始菜单，黄色区域则是应用程序。<br /><br /> <br /><br /> <br /><br />在fbml应用中，facebook平台主要起着中间人的作用，如下图所示，<br /><br /> <br /><br />所有的用户对facebook平台的请求都被转到了应用的url（在application 里可以配callback url），只不过是把所有的请求变为post, 同时加上认证必需的一些参数。最后返回的html显示在facebook的application区的canvas里。<br /><br />但是这个中间人的角色并不象想象中的那么简单， 它至少会引起以下问题：<br /><br />1、httpsession信息丢失，基本上现在的httpsession都依赖于cookie中的jsessionid，但是经过中间人（facebook服务器）后，cookie的信息是无法获取，也意味着httpsession是无法保存信息的。因此所有的用户相关的信息都是加在中转后的reqeust参数里的，从request里的userId 和sessionKey应用程序可以重建出用户的所有信息。 不过这倒反而实现了应用的share nothing architectue，对系统的scalability是有好处的。<br /><br />2、外部css丢失，由于中转的时候只去request callback url里的页面，所有外部的css内容都是干掉的，应用程序只能在html文件里定义css，这对于css狂们真是灾难。<br /><br />3、js丢失，facebook会把页面中所有的js给滤掉，而取而代之用自己设计的有限的安全的js语言，这很重要的一部分原因是安全性。但对于ajax狂来说这又是灾难。<br /><br /> <br /><br />五、facebook的authentication<br /><br />Facebook的认证过程其实不复杂， 但是如果java程序捧着官方提供的java包捣腾，恐怕还要费些功夫，官方那个只提供了桌面应用的认<br /><br />证程序，而web应用的认证过程则大厢径庭。<br /><br />应用程序在注册时会获得该应用的api_key和secret，这实际上是访问该应用的用户名和密码了，只有开发人员可以看到。事实上以后的每次调用facebook api都会带上这api_key，但这显然还不够，登录用户必须拥自己特定的信息：sessionKey，每次调用带上这个key才能将用户的信息关联（类似于tomcat的jsessionid），因此认证的主要目的就是拿到sessionKey。<br /><br /> <br /><br />上图是外部web应用的认证过程，这种应用只通过web services与facebook集成，这种应用唯一要做的是获得取得调用web services的权限，上图的流程很清楚了。如果用java开发，一般先用一个filter或其它interceptor拦截，如果发现没有登录 facebook应用自动导到facebook的login页面（在request的参数里将登录完后回来的页面传进去），登录后跳回到原来的页面，就可以在filter中通过request里的authtoken获取sessionKey了，这种应用一般将认证后的sessionKey放到HttpSession就可以了（以后不用重复认证）。<br /><br /> <br /><br /> <br /><br />上图是fbml应用的认证过程，事实上内部应用的authentication远比外部应用简单，但是官方竟然没提供文档。由于facebook的用户必须登录才能使用，实际上在使用 facebook应用时用户已经登录并拥有了sessionKey和user_id等参数，因此在中间人（facebook server）中转到应用的url时，它在request里会把sessionKey和userId等参数传进去，因此在拿到这些参数后客户端可以直接进行任何web services的调用。<br /><br /> <br /><br />六、facebook api (RESTful web services)<br /><br />Facebook提供了一堆的api，有认证、用户、相册、好友等功能，从使用的角度来说这倒并不存在什么难点。本身REST的api就是一个httpRequest请求过去返回一个xml的response。经过了官方或非官方的包装以后就变成一个简单的方法调用。<br /><br />1、所有的api调用都是无状态的，这也是facebook拥有这么好的 scalability的重要原因。每个request里都会带上api_key, session_key, call_id, sig等参数，这保证了安全性的同时也保证了scalbility。<br /><br />2、java的客户端调用包比起ruby来实在是恶心多了。这个时候动态语言的优势体现得太明显了。利用ruby的method missing功能，一个简单的实现就可以调用facebook的所有api了，而且扩展性好。Java的包里则定义了一堆恶心方法，而且返回的是一个 xml document， 经过一堆的解析才能取到结果。<br /><br /> <br /><br />七、fql与fbml<br /><br />Fql就是facebook版的sql，从使用的角度来说fql的其实挺简单，它只是sql的一个子集， 只支持单表查询，where条件必须是索引过的字段，支持子查询， 还支持一些sql的函数。最简单的例子：<br /><br />SELECT name, pic FROM user WHERE uid=211031 OR uid=4801660<br /><br /> <br /><br />Fbml是facebook提供的一堆tag，它只能在fbml的应用程序中使用。从使用的角度来说也很简单，以下html显示了一个用户头像：<br /><br />&lt;td>&lt;fb:profile-pic uid="${friendId}" linked="true" />&lt;/td><br /><br /> <br /><br />八、开发环境                       <br /><br />开发外部的facebook应用和iframe的facebook应用不需要任何的特殊配置，但是开发fbml的facebook应用却是另一回事了。从第四节的facebook as middleman的图中我们可以看到facebook服务器要读到应用的页面塞到facebook的canvas里。这意味着应用的页面必须是外网可以直接访问才能看到效果。<br /><br />而我们的开发平台却是搭在本地的， 难道只有部署到服务器上才能看到页面效果？这对开发调是一个严重的挑战。<br /><br />后悔我当初没有看到这篇blog， 利用SSH的reverse tunnel功能可以将外网的地址按端口号tunnel到本地的开发环境。<br /><br />http://blog.evanweaver.com/articles/2007/07/13/developing-a-facebook-app-locally<br /><br /> <br /><br />我采用另一种方法：增加middleman，facebook本身起了一个中间人的作用，将facebook的请示导到了应用的请求，为什么我们不可以再增加一个中间人， 把外网的请求导到内网？<br /><br />幸好一个几十行的ruby on rails程序就可以搞定route的功能。<br /><br />这样实现后的配置也很简单，只要将callback url里的参数映射到本地机器就可以实现多人的同时开发：<br /><br />    urlMap={<br /><br />        :xcc=>'http://192.168.80.156/facebook/',<br /><br />        :skt=>'http://192.168.80.133/facebook/'<br /><br />    }<br /><br /> <br /><br /> <br /><br />附  facebook平台开发的非官方tutorial：<br /><br /> <br /><br />Ruby的：<br /><br />http://www.liverail.net/articles/2007/6/29/tutorial-on-developing-a-facebook-platform-application-with-ruby-on-rails<br /><br />http://giantrobots.thoughtbot.com/2007/6/14/fist-in-your-facebook<br /><br /> <br /><br /> <br /><br />java的:<br /><br />http://javablog.co.uk/2007/07/25/tips-for-writing-facebook-applications-in-java<br /><br /> <br /><br />http://tmachine1.dh.bytemark.co.uk/blog/index.php/2007/08/02/how-to-make-facebook-apps-using-java-part-1/<br /><br /> <br /><br />http://tmachine1.dh.bytemark.co.uk/blog/index.php/2007/08/13/how-to-make-facebook-apps-using-java-part-2/<br /><br /> <br /><br />http://tmachine1.dh.bytemark.co.uk/blog/index.php/2007/09/21/ten-tips-and-tricks-for-writing-facebook-apps/
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/177929#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 31 Mar 2008 10:38:34 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/177929</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/177929</guid>
      </item>
      <item>
        <title>TCP-UDP-HTTP-SOCKET的区别</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/177914" style="color:red;">http://liwanchun-xd.javaeye.com/blog/177914</a>&nbsp;
          发表时间: 2008年03月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          TCP、UDP是传输层协议。<br />HTTP是应用层协议，其传输都是被包装成TCP协议传输。<br />SOCKET是实现传输层协议的一种编程API。
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/177914#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 31 Mar 2008 10:18:46 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/177914</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/177914</guid>
      </item>
      <item>
        <title>JBossCache-TreeCache体验</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/177865" style="color:red;">http://liwanchun-xd.javaeye.com/blog/177865</a>&nbsp;
          发表时间: 2008年03月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          TreeCache是一种结构化的、基于复制的事务缓存。TreeCache是JBoss应用服务器中集群服务—包括JNDI集群、HTTP和EJB的Sesssion集群、JMS集群—的基础框架。其可以单独使用，可以集成到JBossAS应用，也可以集成到其他的应用服务器上。TreeCache是一种树状结构，每个节点拥有一个名字和多个或者没有子节点，除跟节点没有子节点其他节点有且只有一个父母节点，可以通过路径名来访问子节点（FQN：Full Qualified Name），在一个TreeCache中可以存在多棵树，，即可以有多个根节点。当应用于分布式环境时，由于TreeCache是基于复制的，每个子节点的值必须是可序列化的。<br />     在下面中，将通过例子来了解TreeCache的功能及其配置，使用JBossCache1.4和JDK5.0。首先是一个最基本使用TreeCache的程序例子并配置一个TreeCache的配置骨架（各种常用的配置可参见jboss-cache-dist-1.4.0.CR1版本的etc目录，如下各种配置参考也可见该目录下的范例配置，以下不再强调），见下：<br /><br />treecache.xml：<br /><br /><br />&lt;server><br />    &lt;mbean code="org.jboss.cache.TreeCache"<br />        name="jboss.cache:service=TreeCache"><br />        &lt;depends>jboss:service=Naming&lt;/depends><br />        &lt;depends>jboss:service=TransactionManager&lt;/depends><br />        &lt;attribute name="ClusterName">TreeCache-Cluster&lt;/attribute><br />        &lt;attribute name="ClusterConfig"><br />            &lt;config><br />                &lt;UDP mcast_addr="228.1.2.3" mcast_port="48866"<br />                    ip_ttl="64" ip_mcast="true" <br />                    mcast_send_buf_size="150000" mcast_recv_buf_size="80000"<br />                    ucast_send_buf_size="150000" ucast_recv_buf_size="80000"<br />                    loopback="false"/><br />                &lt;PING timeout="2000" num_initial_members="3"<br />                    up_thread="false" down_thread="false"/><br />                &lt;MERGE2 min_interval="10000" max_interval="20000"/><br />                &lt;FD_SOCK/><br />                &lt;VERIFY_SUSPECT timeout="1500"<br />                    up_thread="false" down_thread="false"/><br />                &lt;pbcast.NAKACK gc_lag="50" retransmit_timeout="600,1200,2400,4800"<br />                    max_xmit_size="8192" up_thread="false" down_thread="false"/><br />                &lt;UNICAST timeout="600,1200,2400" window_size="100" min_threshold="10"<br />                    down_thread="false"/><br />                &lt;pbcast.STABLE desired_avg_gossip="20000"<br />                    up_thread="false" down_thread="false"/><br />                &lt;FRAG frag_size="8192"<br />                    down_thread="false" up_thread="false"/><br />                &lt;pbcast.GMS join_timeout="5000" join_retry_timeout="2000"<br />                    shun="true" print_local_addr="true"/><br />                &lt;pbcast.STATE_TRANSFER up_thread="true" down_thread="true"/><br />            &lt;/config><br />        &lt;/attribute><br />    &lt;/mbean><br />&lt;/server><br />   其中ClusterConfig配置在前面JavaGroups的介绍详细介绍，其它配置比较简单，需要进一步了解请参见TreeCache文档。<br />   一、Cache分类<br />   TreeCache按功能分为三类：本地(Local)Cache、复制(Replication)Cache和失效(Invalidation)Cache。本地Cache只应用于本地环境，后两个Cache可应用于分布式环境，其中，在分布式环境中，复制Cache当一个Cache实例的一个节点值发生变化时会将变化复制到其它实例中，而失效Cache是当一个Cache实例的一个节点值发生变化时会将其它实例的相应节点的值设为空，让其重新去获得该值，可通过这种方式缓存大对象以减少在实例中复制对象的代价。分布式Cache（复制和失效Cache）又分为两种，同步（REPL_ASYNC）和异步（REPL_SYNC），同步Cache是在一个Cache实例做修改时，等待变化应用到其它实例后才返回，而异步Cache是在一个Cache实例做修改时，即刻返回。其配置见下：<br />!--<br />  Valid modes are LOCAL<br />                  REPL_ASYNC<br />                  REPL_SYNC<br />                  INVALIDATION_ASYNC<br />                  INVALIDATION_SYNC<br />-><br />attribute name="CacheMode">REPL_SYNC&lt;/attribute><br /><br />   二、事务和并行(Transaction And Concurrent)<br />   TreeCache是一种事务Cache，与JDBC一样，其包括两方面内容：锁和隔离级别。锁分为悲观锁和乐观锁，当使用悲观锁时，分为五个隔离级别，分别是SERIALIZABLE、REPEATABLE_READ (default)、READ_COMMITTED、READ_UNCOMMITTED和NONE，隔离级别逐步减弱。乐观锁也叫版本锁，其对数据进行操作时，将其复制到临时区，操作之后将版本与原有数据比较，如果一致则将递增版本并写回，如果不一致则回滚，由于乐观锁仅在复制出数据和提交数据时对数据加锁，所以并行度更高，但如果写操作比较频繁地话则容易出现冲突导致回滚。TreeCache默认使用悲观锁。使用TreeCache时，需要使用容器提供的事务管理器，一般使JBossTransactionManagerLookup和GenericTransactionManagerLookup，前者应用于JBOSS服务器，后者应用于其他服务器，也可使用DummyTransactionManagerLookup用于测试。如上介绍的配置如下：<br />attribute name="NodeLockingScheme">OPTIMISTIC&lt;/attribute><br />      &lt;attribute name="IsolationLevel">REPEATABLE_READ&lt;/attribute><br />     &lt;attribute name="TransactionManagerLookupClass">org.jboss.cache.DummyTransactionManagerLookup&lt;/attribute><br /><br /><br />   三、逐出策略(Eviction Policy)<br />   由于内存数量的局限，不可能将所有的Cache数据存放在内存中，但使用内存达到一定极限时，会将部分数据清除出内存，保存到其它持久媒质中，定义的什么时候清除、如何清除的策略就是逐出策略。自定义一个逐出策略需要实现org.jboss.cache.eviction.EvictionPolicy、org.jboss.cache.eviction.EvictionAlgorithm、<br />g.jboss.cache.eviction.EvictionQueue 和org.jboss.cache.eviction.EvictionConfiguration四个接口，系统提供了LRU(Least recently used，最近最少使用)、LFU(Least Frequently Used最不经常使用)、FIFO(First In First Out先进先出)、MRU(Most Recently Used最近最经常使用)四种实现，详细参见org.jboss.cache.eviction包的源代码。配置如下：<br />&lt;attribute name="EvictionPolicyConfig"><br />   &lt;config><br />      &lt;attribute name="wakeUpIntervalSeconds">5&lt;/attribute><br />      &lt;region name="/_default_"><br />        &lt;attribute name="maxNodes">5000&lt;/attribute><br />        &lt;attribute name="timeToLiveSeconds">1000&lt;/attribute><br />      &lt;/region><br />      &lt;region name="/org/jboss/data"<br />policyClass="org.jboss.cache.eviction.FIFOPolicy"><br />        &lt;attribute name="maxNodes">5000&lt;/attribute><br />      &lt;/region><br />      &lt;region name="/test/" policyClass="org.jboss.cache.eviction.MRUPolicy"><br />        &lt;attribute name="maxNodes">10000&lt;/attribute><br />      &lt;/region><br />      &lt;region name="/maxAgeTest/"><br />        &lt;attribute name="maxNodes">10000&lt;/attribute><br />        &lt;attribute name="timeToLiveSeconds">8&lt;/attribute><br />        &lt;attribute name="maxAgeSeconds">10&lt;/attribute><br />      &lt;/region><br />   &lt;/config><br />&lt;/attribute><br />     四、Cache加载<br />  由于逐出策略的存在，那么当我们重新需要获得一个原来在缓存中但确由内存原因被逐出的数据时，就需要定义一种加载策略，使地可以重新找回数据，同时，Cache加载也肩负在将数据逐出时将数据保存到持久媒质的责任。<br />  根据将数据保存媒质的不同，Cache加载包括FileCacheLoader、JDBCCacheLoader等等，可以同时使用多种加载器来灵活定制加载策略。例见下：<br />&lt;attribute name="CacheLoaderConfiguration"><br />    &lt;config><br />        &lt;passivation>false&lt;/passivation><br />        &lt;preload>/&lt;/preload><br />        &lt;shared>true&lt;/shared><br />        &lt;cacheloader><br />            &lt;class>org.jboss.cache.loader.ClusteredCacheLoader&lt;/class><br />            &lt;properties><br />                 timeout=1000<br />            &lt;/properties><br />            &lt;async>true&lt;/async><br />            &lt;fetchPersistentState>false&lt;/fetchPersistentState><br />            ignoreModifications>false&lt;/ignoreModifications><br />            &lt;purgeOnStartup>false&lt;/purgeOnStartup><br />        &lt;/cacheloader><br />        &lt;cacheloader><br />            &lt;class>org.jboss.cache.loader.JDBCCacheLoader&lt;/class><br />            &lt;properties><br />                        cache.jdbc.table.name=jbosscache<br />                        cache.jdbc.table.create=true<br />                        cache.jdbc.table.drop=true<br />                        cache.jdbc.table.primarykey=jbosscache_pk<br />                        cache.jdbc.fqn.column=fqn<br />                        cache.jdbc.fqn.type=varchar(255)<br />                        cache.jdbc.node.column=node<br />                        cache.jdbc.node.type=longblob<br />                        cache.jdbc.parent.column=parent<br />                        cache.jdbc.driver=com.mysql.jdbc.Driver<br />                        cache.jdbc.url=jdbc:mysql://localhost:3306/jbossdb<br />                        cache.jdbc.user=root<br />                        cache.jdbc.password=<br />             &lt;/properties><br />             &lt;async>true&lt;/async><br />             &lt;fetchPersistentState>false&lt;/fetchPersistentState><br />             &lt;purgeOnStartup>false&lt;/purgeOnStartup><br />         &lt;/cacheloader><br />    &lt;/config><br />&lt;/attribute><br /><br /> 我们将通过定制如上的配置信息以更有效地使用JBossCache。详细情况可参考JBoss TreeCache参考文档和范例。 <br /><br />TreeCache tree = new TreeCache();<br />tree.setClusterProperties("treecache.xml"<img src="/images/wink.gif" />; <br />tree.createService(); <br />tree.startService(); <br />tree.put("/a/b/c", "name", "Ben"<img src="/images/wink.gif" />;<br />tree.put("/a/b/c/d", "uid", new Integer(322649));<br />Integer tmp = (Integer) tree.get("/a/b/c/d", "uid"<img src="/images/wink.gif" />;<br />tree.remove("/a/b"<img src="/images/wink.gif" />;<br />tree.stopService();<br />tree.destroyService();
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/177865#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 30 Mar 2008 22:49:13 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/177865</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/177865</guid>
      </item>
      <item>
        <title>Hibernate之Cache学习笔记</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/177853" style="color:red;">http://liwanchun-xd.javaeye.com/blog/177853</a>&nbsp;
          发表时间: 2008年03月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Hibernate 中实现了良好的Cache 机制，我们可以借助Hibernate 内部的Cache迅速提高系统数据读取性能。<br /><br />需要注意的是：Hibernate做为一个应用级的数据访问层封装，只能在其作用范围内保持Cache中数据的的有效性，也就是说，在我们的系统与第三方系统共享数据库的情况下，Hibernate的Cache机制可能失效。一个很简单的例子，如果你用access修改了库中的值，那么这就不会更新JVM中的缓冲池，这就导致了赃数据的产生。<br /><br />Hibernate 在本地JVM 中维护了一个缓冲池，并将从数据库获得的数据保存到池中以供下次重复使用（如果在Hibernate中数据发生了变动，Hibernate同样也会更新池中的数据版本）。此时，如果有第三方系统对数据库进行了更改，那么，Hibernate并不知道数据库中的数据已经发生了变化，也就是说，池中的数据还是修改之前的版本，下次读取时，Hibernate会将此数据返回给上层代码，从而导致潜在的问题。外部系统的定义，并非限于本系统之外的第三方系统，即使在本系统中，如果出现了绕过Hibernate数据存储机制的其他数据存取手段，那么Cache的有效性也必须细加考量。如，在同一套系统中，基于Hibernate和基于JDBC的两种数据访问方式并存，那么通过JDBC更新数据库的时候，Hibernate同样无法获知数据更新的情况，从而导致脏数据的出现。<br /><br />基于Java 的Cache 实现，最简单的莫过于HashTable，hibernate 提供了基于Hashtable 的Cache 实现机制，不过，由于其性能和功能上的局限，仅供开发调试中使用。同时，Hibernate 还提供了面向第三方Cache 实现的接口，如JCS、EHCache、OSCache、JBoss Cache、SwarmCache等。<br /><br />Hibernate中的Cache大致分为两层，第一层Cache在Session实现，属于事务级数据缓冲，一旦事务结束，这个Cache 也就失效。此层Cache 为内置实现，无需我们进行干涉。第二层Cache，是Hibernate 中对其实例范围内的数据进行缓存的管理容器。<br /><br />我们主要学习第二层Cache。<br /><br />Hibernate早期版本中采用了JCS（Java Caching System －Apache Turbine项目中的一个子项目）作为默认的第二层Cache实现。由于JCS的发展停顿，以及其内在的一些问题（在某些情况下，可能导致内存泄漏以及死锁），新版本的Hibernate已经将JCS去除，并用EHCache作为其默认的第二级Cache实现。相对JCS，EHCache更加稳定，并具备更好的缓存调度性能，缺陷是目前还无法做到分布式缓存，如果我们的系统需要在多台设备上部署，并共享同一个数据库，必须使用支持分布式缓存的Cache实现（如JCS、JBossCache）以避免出现不同系统实例之间缓存不一致而导致脏数据的情况。Hibernate对Cache进行了良好封装，透明化的Cache机制使得我们在上层结构的实现中无需面对繁琐的Cache维护细节。<br /><br />目前Hibernate支持的Cache实现有：<br /><br />HashTable：net.sf.hibernate.cache.HashtableCacheProvider  支持查询缓冲。<br /><br />EHCache：net.sf.ehcache.hibernate.Provider 支持查询缓冲。<br /><br />OSCache：net.sf.hibernate.cache.OSCacheProvider 支持查询缓冲。<br /><br />SwarmCache：net.sf.hibernate.cache.SwarmCacheProvider 支持集群。<br /><br />JBossCache：net.sf.hibernate.cache.TreeCacheProvider 支持集群。<br /><br />其中SwarmCache 提供的是invalidation 方式的分布式缓存，即当集群中的某个节点更新了缓存中的数据，即通知集群中的其他节点将此数据废除，之后各个节点需要用到这个数据的时候，会重新从数据库中读入并填充到缓存中。而JBossCache提供的是Reapplication式的缓冲，即如果集群中某个节点的数据发生改变，此节点会将发生改变的数据的最新版本复制到集群中的每个节点中以保持所有节点状态一致。<br /><br />使用第二层Cache，需要在hibernate的配置文件进行配置（省略）主要介绍一下cache策略<br /><br />cache策略可选值有以下几种：<br /><br />1． read-only 只读。<br />2． read-write 可读可写。<br />3． nonstrict-read-write  如果程序对并发数据修改要求不是非常严格，只是偶尔需要更新数据，可以采用本选项，以减少无谓的检查，获得较好的性能。<br />4． transactional 事务性cache。在事务性Cache 中，Cache 的相关操作也被添加到事务之中，如果由于某种原因导致事务失败，我们可以连同缓冲池中的数据一同回滚到事务开始之前的状态。目前Hibernate 内置的Cache 中，只有JBossCache 支持事务性的Cache实现。<br /><br />其他参数简介：<br /><br />maxElementsInMemory="10000"     //Cache中最大允许保存的数据数量<br />eternal="false"                                      //Cache中数据是否为常量<br />timeToIdleSeconds="120"                 //缓存数据钝化时间<br />timeToLiveSeconds="120"                //缓存数据的生存时间<br />overflowToDisk="true"                        //内存不足时，是否启用磁盘缓存<br /><br /><br />需要注意的是Hibernate 的数据库查询机制。我们从查询结果中取出数据的时候，用的最多的是两个方法：Query.list();Query.iterate();<br /><br />对于list方法而言，实际上Hibernate是通过一条Select SQL获取所有的记录。并将其读出，填入到POJO中返回。<br /><br />而iterate 方法，则是首先通过一条Select SQL 获取所有符合查询条件的记录的id，再对这个id 集合进行循环操作，通过单独的Select SQL 取出每个id 所对应的记录，之后填入POJO中返回。<br /><br />也就是说，对于list 操作，需要一条SQL 完成。而对于iterate 操作，需要n+1条SQL。<br /><br />看上去iterate方法似乎有些多余，但在不同的情况下确依然有其独特的功效，如对海量数据的查询，如果用list方法将结果集一次取出，内存的开销可能无法承受。另一方面，对于我们现在的Cache机制而言，list方法将不会从Cache中读取数据，它总是一次性从数据库中直接读出所有符合条件的记录。而iterate 方法因为每次根据id获取数据，这样的实现机制也就为从Cache读取数据提供了可能，hibernate首先会根据这个id 在本地Cache 内寻找对应的数据，如果没找到，再去数据库中检索。<br /><br /><br />资料：Hibernate 开发指南
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/177853#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 30 Mar 2008 22:10:58 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/177853</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/177853</guid>
      </item>
      <item>
        <title>Java数据结构之B树（二叉搜索树）</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/177733" style="color:red;">http://liwanchun-xd.javaeye.com/blog/177733</a>&nbsp;
          发表时间: 2008年03月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          B树（二叉搜索树）定义：<br />1）、每个非叶子节点至多有两个子节点。<br />2）、每个节点都存储关键字值。<br />3）、其左子节点的关键字值小于该节点，且右子节点的关键字值大于或等于该节点。<br /><br />简略代码实现：<br />/**<br /> * 节点类 <br /> */<br />class Node{<br />	public int key;<br />	public int data;<br />	public Node leftChild;<br />	public Node rightChild;<br />	<br />	public Node(int key, int data){<br />		this.key = key;<br />		this.data = data;<br />		this.leftChild = null;<br />		this.rightChild = null;<br />	}<br />	<br />	public void display(){<br />		System.out.println("key: " + key + ", data: " + data);<br />	}<br />}<br /><br />/**<br /> * B树类<br /> */<br />class Tree{<br />	public Node root;<br />	<br />	public void insert(int key, int data){<br />		Node newNode = new Node(key, data);<br /><br />		if (root == null){<br />			root = newNode;<br />		}else{<br />			Node current = root;<br />			Node parent = null;<br />			while (true){<br />				parent = current;<br />				if (key &lt; current.key){	<br />					current = current.leftChild;<br />					if (current == null){<br />						parent.leftChild = newNode;<br />						return;<br />					}<br />				}else{<br />					current = current.rightChild;<br />					if (current == null){<br />						parent.rightChild = newNode;<br />						return;<br />					}<br />				}				<br />			}			<br />		}	<br />	}<br />	<br />	/** 只实现有一个节点的删除 */<br />	public boolean delete(int key){<br />		Node current = root;<br />		Node parent = null;<br />		boolean isLeftChild = false;<br />		<br />		while (current.key != key){<br />			parent = current;<br />			if (key &lt; current.key){<br />				current = current.leftChild;<br />				isLeftChild = true;<br />			}else{<br />				current = current.rightChild;<br />				isLeftChild = false;<br />			}<br />		}<br />		<br />		if (current == null){<br />			return false;<br />		}<br />		<br />		/** 无子节点 */<br />		if (current.leftChild == null && current.rightChild == null){<br />			if (current == root){<br />				root = null;<br />			}else if (isLeftChild){<br />				parent.leftChild = null;<br />			}else{<br />				parent.rightChild = null;<br />			}<br />		}<br />		/** 仅有右节点 */<br />		else if ((current.leftChild == null && current.rightChild != null)){<br />			if (current == root){<br />				root = current.rightChild;<br />			}else if (isLeftChild){<br />				parent.leftChild = current.rightChild;<br />			}else{<br />				parent.rightChild = current.rightChild;<br />			}<br />		}else if ((current.leftChild != null && current.rightChild == null)){<br />			if (current == root){<br />				root = null;<br />			}else if (isLeftChild){<br />				parent.leftChild = current.leftChild;<br />			}else{<br />				parent.rightChild = current.leftChild;<br />			}<br />		}<br />		return true;<br />	}<br />	<br />	public Node find(int key){<br />		Node current = root;<br />		while (current != null){<br />			if (current.key == key){<br />				break;<br />			}else if (key &lt; current.key){<br />				current = current.leftChild;<br />			}else{<br />				current = current.rightChild;<br />			}<br />		}<br />		<br />		return current;<br />	}<br />	<br />	/** 中序 */<br />	public void inOrder(Node localNode){<br />		if (localNode  != null){<br />			inOrder(localNode.leftChild);<br />			System.out.println("key: " + localNode.key + ", data: " + localNode.data);<br />			inOrder(localNode.rightChild);<br />		}<br />		<br />	}<br />	<br />}<br /><br />public class BTree {<br /><br />	/**<br />	 * @param args<br />	 */<br />	public static void main(String[] args) {<br />		// TODO Auto-generated method stub<br />		Tree newTree = new Tree();<br />		newTree.insert(5, 5);<br />		newTree.insert(1, 1);<br />		newTree.insert(2, 2);<br />		newTree.insert(8, 8);<br />		newTree.insert(9, 9);<br />		newTree.insert(7, 7);<br />		<br />		newTree.delete(1);<br />		newTree.inOrder(newTree.root);<br />	}<br /><br />}
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/177733#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 30 Mar 2008 10:44:14 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/177733</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/177733</guid>
      </item>
      <item>
        <title>Java数据结构温习之双向双端链表</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/177730" style="color:red;">http://liwanchun-xd.javaeye.com/blog/177730</a>&nbsp;
          发表时间: 2008年03月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          package com.study.doublelink;<br /><br /><br />/**<br /> * 节点类 <br /> */<br />class Link{<br />	public int data;<br />	public Link next;<br />	public Link previous;<br />	<br />	public Link(int data){<br />		this.data = data;<br />		this.next = null;<br />		this.previous = null;<br />	}<br />	<br />	public void display(){<br />		System.out.println(this.data);<br />	}<br />}<br /><br />/**<br /> * 双向双端链表类<br /> */<br />class DoubleLinkList{<br />	private Link first;<br />	private Link last;<br />	<br />	public DoubleLinkList(){<br />		this.first = null;<br />		this.last = null;<br />	}<br />	<br />	public boolean isEmpty(){<br />		return (first == null);<br />	}<br />	<br />	public void insertFirst(int data){<br />		Link newLink = new Link(data);<br />		if (isEmpty()){<br />			this.last = newLink;<br />		}else{<br />			this.first.previous = newLink;<br />		}<br />		<br />		newLink.next = this.first;<br />		this.first = newLink;		<br />	}<br />	<br />	public boolean insertAfter(int key, int data){<br />		Link current = this.first;<br />		Link newLink = new Link(data);<br />		<br />		while (current.data != key){<br />			current = current.next;<br />			if (current == null){<br />				return false;<br />			}<br />		}<br />		<br />		if (current == this.last){<br />			this.last = newLink;<br />			newLink.next = null;<br />		}else{<br />			current.next.previous = newLink;<br />			newLink.next = current.next;<br />		}<br />		<br />		current.next = newLink;<br />		newLink.previous = current;		<br />		return true;<br />	}<br />	<br />	public void display(){<br />		Link current = this.first;<br />		System.out.println("normal: ");<br />		while (current != null){<br />			current.display();<br />			current = current.next;<br />		}<br />	}<br />	<br />	public void displayReverse(){<br />		Link current = this.last;<br />		System.out.println("reverse: ");<br />		while (current != null){<br />			current.display();<br />			current = current.previous;<br />		}<br />	}<br />}<br /><br /><br />public class DoubleLink {<br /><br />	/**<br />	 * @param args<br />	 */<br />	public static void main(String[] args) {<br />		// TODO Auto-generated method stub<br />		DoubleLinkList dll = new DoubleLinkList();<br />		dll.insertFirst(2);<br />		dll.insertAfter(2, 4);<br />		dll.insertAfter(1, 3);<br />		dll.insertAfter(4, 5);<br />		<br />		dll.display();<br />		dll.displayReverse();		<br />	}<br />}
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/177730#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 30 Mar 2008 10:40:18 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/177730</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/177730</guid>
      </item>
      <item>
        <title>Java数据结构温习之哈希表-链地址法</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/177729" style="color:red;">http://liwanchun-xd.javaeye.com/blog/177729</a>&nbsp;
          发表时间: 2008年03月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          哈希表分为两大类，一是开放地址法，二是链地址法。<br />1）、开放地址法中，通过在哈希表中再找一个空位解决冲突问题。<br />2）、链地址法中，某个数据项的关键字值还是像通常一样映射到哈希表的单元，而数据项本身插入到这个单元的链表中，其他同样映射到该位置的数据项只需要加入到链表中。<br /><br />链地址法Java简缩代码：<br /><br />/**<br />* 节点类<br />*/<br />class Link{<br />	private int data;<br />	public Link next;<br />	<br />	public Link(int data){<br />		this.data = data;<br />		this.next = null;<br />	}<br />	<br />	public int getKey(){<br />		return this.data;<br />	}<br />	<br />	public void display(){<br />		System.out.println(this.data);<br />	}	<br />}<br /><br />/**<br />* 优先级链表<br />*/<br />class SortedList{<br />	private Link first;<br />	<br />	public SortedList(){<br />		this.first = null;<br />	}<br />	<br />	public void insert(Link theLink){<br />		int key = theLink.getKey();<br />		Link current = this.first;<br />		Link previous = null;<br />		<br />		while (current != null && key > current.getKey()){<br />			previous = current;<br />			current = current.next;<br />		}<br />		<br />		if (previous != null){<br />			previous.next = theLink;<br />		}else{<br />			this.first = theLink;<br />		}<br />		<br />		theLink.next = current;<br />	}<br />	<br />	public Link find(int key){<br />		Link current = this.first;<br />		while (current != null && key != current.getKey()){<br />			current = current.next;<br />		}<br />		<br />		return current;<br />	}<br />	<br />	public void display(){<br />		Link current = this.first;<br />		while (current != null){<br />			current.display();<br />			current = current.next;<br />		}<br />	}<br />}<br /><br />/**<br />* 链地址哈希表<br />*/<br />class HashTableLink{<br />	private SortedList[] hashArray;<br />	private int arraySize;<br />	<br />	public HashTableLink(int size){<br />		this.arraySize = size;<br />		this.hashArray = new SortedList[this.arraySize];<br />		for (int i = 0; i &lt; this.arraySize; i++){<br />			this.hashArray[i] = new SortedList();<br />		}<br />	}<br />	<br />	public int keyFunc(int key){<br />		return key % this.arraySize;<br />	}<br />	<br />	public void insert(Link theLink){<br />		int key = keyFunc(theLink.getKey());<br />		this.hashArray[key].insert(theLink);<br />	}<br />	<br />	public Link find(int key){<br />		key = keyFunc(key);<br />		return this.hashArray[key].find(key);<br />	}<br />	<br />	public void display(){<br />		for (int i = 0; i &lt; this.arraySize; i++){<br />			System.out.println("i: " + i);<br />			this.hashArray[i].display();<br />		}<br />	}<br />		<br />}<br /><br />public class HashTableTest {<br /><br />	/**<br />	 * @param args<br />	 */<br />	public static void main(String[] args) {<br />		// TODO Auto-generated method stub<br />		HashTableLink htl = new HashTableLink(3);<br />		for (int i = 1; i &lt; 12; i ++){<br />			Link link = new Link(i);<br />			htl.insert(link);<br />		}<br />		<br />		htl.display();<br />		<br />	}<br /><br />}
          <br/>
          <span style="color:red;">
            <a href="http://liwanchun-xd.javaeye.com/blog/177729#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 30 Mar 2008 10:35:59 +0800</pubDate>
        <link>http://liwanchun-xd.javaeye.com/blog/177729</link>
        <guid>http://liwanchun-xd.javaeye.com/blog/177729</guid>
      </item>
      <item>
        <title>Linux系统下tomcat服务中中文参数乱码问题</title>
        <author>liwanchun_xd</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://liwanchun-xd.javaeye.com">liwanchun_xd</a>&nbsp;
          链接：<a href="http://liwanchun-xd.javaeye.com/blog/176618" style="color:red;">http://liwanchun-xd.javaeye.com/blog/176618</a>&nbsp;
          发表时间: 2008年03月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          现有个项目，运行于Linux平台下的tomcat服务中，在页面中查看传递的中文参数值，是乱码。解决方案如下,<br />第一种方法：