﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-liuzheng</title><link>http://www.blogjava.net/liuzheng/</link><description /><language>zh-cn</language><lastBuildDate>Sun, 12 Apr 2026 21:51:28 GMT</lastBuildDate><pubDate>Sun, 12 Apr 2026 21:51:28 GMT</pubDate><ttl>60</ttl><item><title>Spring-LDAP 的例子</title><link>http://www.blogjava.net/liuzheng/articles/267355.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 24 Apr 2009 06:31:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/267355.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/267355.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/267355.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/267355.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/267355.html</trackback:ping><description><![CDATA[<h3 title=""><a href="http://wangyaodi.javaeye.com/blog/202514">Spring-LDAP</a></h3>
<p>下载地址 ：<a href="http://www.springframework.org/ldap">http://www.springframework.org/ldap</a></p>
<p>用Spring LDAP最小需要：</p>
<ul>
    <li>spring-ldap（spring－ldap包） </li>
    <li>spring－core（用于框架内部的丰富的工具类） </li>
    <li>spring－beans（方便操作java beans的接口和类） </li>
    <li>spring－context（增加通过一致API为应用对象获取资源的能力） </li>
    <li>spring－dao（使经常性的错误处理跟使用中的数据访问分开的异常处理机制） </li>
    <li>commons－logging（简单的日志处理，内部使用） </li>
</ul>
<p>UserDaoLdapImpl</p>
<div>
<div>
<div>Java代码 <embed src="http://wangyaodi.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=package%20cn.com.ldap%3B%0A%0Aimport%20java.util.List%3B%0A%0Aimport%20javax.naming.NamingException%3B%0Aimport%20javax.naming.directory.Attributes%3B%0A%0Aimport%20org.springframework.ldap.core.AttributesMapper%3B%0Aimport%20org.springframework.ldap.core.LdapTemplate%3B%0A%0Aimport%20cn.com.ldap.Preson.Person%3B%0A%0A%2F**%0A%20*%20%40author%20Wangyaodi%20version%201.0%202008-6-12%20%7C%20%E4%B8%8B%E5%8D%8802%3A55%3A25%0A%20*%2F%0Apublic%20class%20UserDaoLdapImpl%20%7B%0A%09private%20LdapTemplate%20ldapTemplate%3B%0A%0A%09public%20void%20setLdapTemplate(LdapTemplate%20ldapTemplate)%20%7B%0A%09%09this.ldapTemplate%20%3D%20ldapTemplate%3B%0A%09%7D%0A%0A%09public%20List%20getAllPersonNames()%20%7B%0A%09%09return%20ldapTemplate.search(%22%22%2C%20%22(objectclass%3Dperson)%22%2C%0A%09%09%09%09new%20AttributesMapper()%20%7B%0A%09%09%09%09%09public%20Object%20mapFromAttributes(Attributes%20attrs)%0A%09%09%09%09%09%09%09throws%20NamingException%20%7B%0A%09%09%09%09%09%09return%20attrs.get(%22cn%22).get()%3B%0A%09%09%09%09%09%7D%0A%09%09%09%09%7D)%3B%0A%09%7D%0A%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol start="1">
    <li>package&nbsp;cn.com.ldap;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>import&nbsp;java.util.List;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>import&nbsp;javax.naming.NamingException;&nbsp;&nbsp;</li>
    <li>import&nbsp;javax.naming.directory.Attributes;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.ldap.core.AttributesMapper;&nbsp;&nbsp;</li>
    <li>import&nbsp;org.springframework.ldap.core.LdapTemplate;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>import&nbsp;cn.com.ldap.Preson.Person;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>/**&nbsp;</li>
    <li>&nbsp;*&nbsp;@author&nbsp;Wangyaodi&nbsp;version&nbsp;1.0&nbsp;2008-6-12&nbsp;|&nbsp;下午02:55:25&nbsp;</li>
    <li>&nbsp;*/&nbsp;&nbsp;</li>
    <li>public&nbsp;class&nbsp;UserDaoLdapImpl&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;LdapTemplate&nbsp;ldapTemplate;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;setLdapTemplate(LdapTemplate&nbsp;ldapTemplate)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.ldapTemplate&nbsp;=&nbsp;ldapTemplate;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;List&nbsp;getAllPersonNames()&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ldapTemplate.search("",&nbsp;"(objectclass=person)",&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;AttributesMapper()&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Object&nbsp;mapFromAttributes(Attributes&nbsp;attrs)&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;NamingException&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;attrs.get("cn").get();&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">package cn.com.ldap;
import java.util.List;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import cn.com.ldap.Preson.Person;
/**
* @author Wangyaodi version 1.0 2008-6-12 | 下午02:55:25
*/
public class UserDaoLdapImpl {
private LdapTemplate ldapTemplate;
public void setLdapTemplate(LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
public List getAllPersonNames() {
return ldapTemplate.search("", "(objectclass=person)",
new AttributesMapper() {
public Object mapFromAttributes(Attributes attrs)
throws NamingException {
return attrs.get("cn").get();
}
});
}
}</pre>
<p>preson:</p>
<div>
<div>
<div>Java代码 <embed src="http://wangyaodi.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=package%20cn.com.ldap%3B%0A%0Apublic%20class%20Preson%20%7B%0A%09public%20class%20Person%20%7B%0A%0A%09%09private%20String%20cn%3B%0A%0A%09%09private%20String%20sn%3B%0A%0A%09%09public%20String%20getCn()%20%7B%0A%09%09%09return%20cn%3B%0A%09%09%7D%0A%0A%09%09public%20void%20setCn(String%20cn)%20%7B%0A%09%09%09this.cn%20%3D%20cn%3B%0A%09%09%7D%0A%0A%09%09public%20String%20getSn()%20%7B%0A%09%09%09return%20sn%3B%0A%09%09%7D%0A%0A%09%09public%20void%20setSn(String%20sn)%20%7B%0A%09%09%09this.sn%20%3D%20sn%3B%0A%09%09%7D%0A%0A%09%7D%0A%0A%7D%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol start="1">
    <li>package&nbsp;cn.com.ldap;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>public&nbsp;class&nbsp;Preson&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;class&nbsp;Person&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;cn;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;sn;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;getCn()&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;cn;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;setCn(String&nbsp;cn)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.cn&nbsp;=&nbsp;cn;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;getSn()&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;sn;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;setSn(String&nbsp;sn)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.sn&nbsp;=&nbsp;sn;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>}&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="java">package cn.com.ldap;
public class Preson {
public class Person {
private String cn;
private String sn;
public String getCn() {
return cn;
}
public void setCn(String cn) {
this.cn = cn;
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
}
}
</pre>
<p>applicationContext.xml</p>
<div>
<div>
<div>Xml代码 <embed src="http://wangyaodi.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A%3C!DOCTYPE%20beans%20PUBLIC%20%22-%2F%2FSPRING%2F%2FDTD%20BEAN%2F%2FEN%22%20%22http%3A%2F%2Fwww.springframework.org%2Fdtd%2Fspring-beans.dtd%22%3E%0A%3Cbeans%3E%0A%09%3Cbean%20id%3D%22contextSource%22%0A%09%09class%3D%22org.springframework.ldap.core.support.LdapContextSource%22%3E%0A%09%09%3Cproperty%20name%3D%22url%22%20value%3D%22ldap%3A%2F%2F192.168.16.XXX%3A389%22%20%2F%3E%0A%09%09%3Cproperty%20name%3D%22base%22%20value%3D%22OU%3DXXX%2COU%3DXXX%2COU%3DXXX%2COU%3DXXX%2CDC%3DXXX%2CDC%3DXXX%2CDC%3DXXX%22%20%2F%3E%0A%09%09%3Cproperty%20name%3D%22userName%22%20value%3D%22XXX%40headquarter%22%20%2F%3E%0A%09%09%3Cproperty%20name%3D%22password%22%20value%3D%22XXX%22%20%2F%3E%0A%09%3C%2Fbean%3E%0A%0A%09%3Cbean%20id%3D%22ldapTemplate%22%0A%09%09class%3D%22org.springframework.ldap.core.LdapTemplate%22%3E%0A%09%09%3Cconstructor-arg%20ref%3D%22contextSource%22%20%2F%3E%0A%09%3C%2Fbean%3E%0A%0A%09%3Cbean%20id%3D%22userDao%22%20class%3D%22cn.com.ldap.UserDaoLdapImpl%22%3E%0A%09%09%3Cproperty%20name%3D%22ldapTemplate%22%3E%0A%09%09%09%3Cref%20bean%3D%22ldapTemplate%22%20%2F%3E%0A%09%09%3C%2Fproperty%3E%0A%09%3C%2Fbean%3E%0A%3C%2Fbeans%3E" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol start="1">
    <li>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt;&nbsp;&nbsp;</li>
    <li>&lt;!DOCTYPE&nbsp;beans&nbsp;PUBLIC&nbsp;"-//SPRING//DTD&nbsp;BEAN//EN"&nbsp;"http://www.springframework.org/dtd/spring-beans.dtd"&gt;&nbsp;&nbsp;</li>
    <li>&lt;beans&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean&nbsp;id="contextSource"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class="org.springframework.ldap.core.support.LdapContextSource"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="url"&nbsp;value="ldap://192.168.16.XXX:389"&nbsp;/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="base"&nbsp;value="OU=XXX,OU=XXX,OU=XXX,OU=XXX,DC=XXX,DC=XXX,DC=XXX"&nbsp;/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="userName"&nbsp;value="XXX@headquarter"&nbsp;/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="password"&nbsp;value="XXX"&nbsp;/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean&nbsp;id="ldapTemplate"&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class="org.springframework.ldap.core.LdapTemplate"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;constructor-arg&nbsp;ref="contextSource"&nbsp;/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean&nbsp;id="userDao"&nbsp;class="cn.com.ldap.UserDaoLdapImpl"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="ldapTemplate"&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref&nbsp;bean="ldapTemplate"&nbsp;/&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;&nbsp;&nbsp;</li>
    <li>&lt;/beans&gt;&nbsp;&nbsp;</li>
</ol>
</div>
<pre style="display: none;" name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"&gt;
&lt;beans&gt;
&lt;bean id="contextSource"
class="org.springframework.ldap.core.support.LdapContextSource"&gt;
&lt;property name="url" value="ldap://192.168.16.XXX:389" /&gt;
&lt;property name="base" value="OU=XXX,OU=XXX,OU=XXX,OU=XXX,DC=XXX,DC=XXX,DC=XXX" /&gt;
&lt;property name="userName" value="XXX@headquarter" /&gt;
&lt;property name="password" value="XXX" /&gt;
&lt;/bean&gt;
&lt;bean id="ldapTemplate"
class="org.springframework.ldap.core.LdapTemplate"&gt;
&lt;constructor-arg ref="contextSource" /&gt;
&lt;/bean&gt;
&lt;bean id="userDao" class="cn.com.ldap.UserDaoLdapImpl"&gt;
&lt;property name="ldapTemplate"&gt;
&lt;ref bean="ldapTemplate" /&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;/beans&gt;</pre>
<p>main&nbsp;</p>
<div>
<div>
<div>Java代码 <embed src="http://wangyaodi.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=public%20static%20void%20main(String%5B%5D%20args)%20%7B%0A%09%09ApplicationContext%20cxt%20%3D%20new%20ClassPathXmlApplicationContext(%22applicationContext.xml%22)%3B%0A%09%09UserDaoLdapImpl%20userDao%20%3D%20(UserDaoLdapImpl)cxt.getBean(%22userDao%22)%3B%0A%09%09List%20users%20%3D%20userDao.getAllPersonNames()%3B%0A%09%09System.out.println(users.size())%3B%0A%09%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="14" height="15"></div>
</div>
<ol start="1">
    <li>public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplicationContext&nbsp;cxt&nbsp;=&nbsp;new&nbsp;ClassPathXmlApplicationContext("applicationContext.xml");&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserDaoLdapImpl&nbsp;userDao&nbsp;=&nbsp;(UserDaoLdapImpl)cxt.getBean("userDao");&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&nbsp;users&nbsp;=&nbsp;userDao.getAllPersonNames();&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(users.size());&nbsp;&nbsp;</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <br />
    </li>
</ol>
</div>
<img src ="http://www.blogjava.net/liuzheng/aggbug/267355.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2009-04-24 14:31 <a href="http://www.blogjava.net/liuzheng/articles/267355.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>经典的Ldap的组织图</title><link>http://www.blogjava.net/liuzheng/articles/267327.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 24 Apr 2009 03:49:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/267327.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/267327.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/267327.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/267327.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/267327.html</trackback:ping><description><![CDATA[<p>Data must be structured for LDAP. Our internet-domain is zirndorf.de, so
I use that. Under that there is a unit in which all the people are.</p>
<p><img src="http://lena.franken.de/ldap/simple_structure.gif" alt="simple_structure.gif" width="715" border="0" height="1025" /></p>
<img src ="http://www.blogjava.net/liuzheng/aggbug/267327.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2009-04-24 11:49 <a href="http://www.blogjava.net/liuzheng/articles/267327.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ldap 很好的学习网站</title><link>http://www.blogjava.net/liuzheng/articles/267317.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 24 Apr 2009 03:08:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/267317.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/267317.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/267317.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/267317.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/267317.html</trackback:ping><description><![CDATA[http://www.zytrax.com/books/ldap/
<img src ="http://www.blogjava.net/liuzheng/aggbug/267317.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2009-04-24 11:08 <a href="http://www.blogjava.net/liuzheng/articles/267317.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ldap schema 自定义</title><link>http://www.blogjava.net/liuzheng/articles/267316.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 24 Apr 2009 03:07:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/267316.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/267316.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/267316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/267316.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/267316.html</trackback:ping><description><![CDATA[<p>An <strong>LDAP schema</strong> is nothing more than a convenient packaging unit for containing broadly similar <a title="" href="http://www.zytrax.com/books/ldap/apd/index.html#objectclasses" class="t-db">objectClasses</a> and <a title="" href="http://www.zytrax.com/books/ldap/apd/index.html#attributes" class="t-db">attributes</a>.</p>
<p>There may have been a time when a single schema was designed to hold
everything required for an LDAP implementation (like a relational
database schema) but that is no longer true. You will find useful
attributes and objectclases scattered all over the place - the power of
LDAP arguably comes from the ease of creating and using this apparent
anarchy.</p>
<p>The rule is: Every attribute or objectclass (including its superior
objectclass or attribute) used in an LDAP implementation must be
defined in a <strong>schema</strong> and that schema must be <strong>known</strong> to the LDAP server. In OpenLDAP the schemas are made known using the <a href="http://www.zytrax.com/books/ldap/ch6/index.html#include" class="t-db">include</a> statement in the <a title="" href="http://www.zytrax.com/books/ldap/ch6" class="t-db">slapd.conf</a> configuration file).</p>
<p>The following diagram illustrates the use of schemas as packaging units:</p>
<p align="center"><img title="" src="http://www.zytrax.com/books/ldap/images/ldap-schemas-packaging.gif" alt="LDAP - Schema, objectClasses and Attributes" border="0" />
</p>
<p><a href="http://www.zytrax.com/books/ldap/ch3/#contents"><img src="http://www.zytrax.com/images/go_up.gif" alt="Up Arrow" border="0" /></a></p>
<h2>3.3 LDAP objectClasses</h2>
<p>An <strong>objectClass</strong> is a collection of attributes (or an attribute container) and has the following characteristics:</p>
<ol>
    <li>
    <p>An <strong>objectclass</strong> is defined within a <strong>Schema</strong></p>
    </li>
    <li>
    <p>An <strong>objectclass</strong> may be a part of an objectclass hierarchy in which case it inherits all the properties of its parents, for example, <a title="" href="http://www.zytrax.com/books/ldap/ape/inetorgperson.html#inetorgperson" class="t-db">inetOrgPerson</a> is the child of <a title="" href="http://www.zytrax.com/books/ldap/ape/core-schema.html#organizationalperson" class="t-db">organizationalPerson</a> which is the child of <a title="" href="http://www.zytrax.com/books/ldap/ape/core-schema.html#person" class="t-db">person</a> which is the child of <strong>top</strong> (the ABSTRACT objectClass which terminates every objectClass hirearchy).</p>
    </li>
    <li>
    <p>An <strong>objectclass</strong> has a globally unique name or identifier</p>
    </li>
    <li>
    <p>An <strong>objectclass</strong>, as well as being an attribute container, is also an attribute and may be searched on</p>
    </li>
    <li>
    <p>An <strong>objectclass</strong> defines its member attributes and whether these MUST (mandatory) be present or MAY (optional) be present in an entry.</p>
    </li>
    <li>
    <p>One or more <strong>objectclass(es)</strong> must be present in an LDAP <a href="http://www.zytrax.com/books/ldap/apd/index.html#entry" class="t-db">entry</a>.</p>
    </li>
    <li>
    <p>Each <strong>objectclass</strong> supported by a LDAP server forms part of a <strong>collection</strong> called <strong>objectclasses</strong> which can be discovered via the <a title="" href="http://www.zytrax.com/books/ldap/ch3/#operational" class="t-db"><strong>subschema</strong></a>.</p>
    </li>
</ol>
<h3>Defining an objectClass</h3>
<p>The formal objectclass definition is defined in <a href="http://www.zytrax.com/books/ldap/apc/rfc2252.txt" class="t-db">RFC 2252 section 4.4</a> and looks like this:</p>
<pre>ObjectClassDescription = "(" whsp<br />
numericoid whsp      ; ObjectClass identifier<br />
[ "NAME" qdescrs ]<br />
[ "DESC" qdstring ]<br />
[ "OBSOLETE" whsp ]<br />
[ "SUP" oids ]       ; Superior ObjectClasses<br />
[ ( "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" ) whsp ]<br />
; default structural<br />
[ "MUST" oids ]      ; AttributeTypes<br />
[ "MAY" oids ]       ; AttributeTypes<br />
whsp ")"<br />
</pre>
<p>Ooof! <strong>whsp</strong> means a space character and when they say it
should be there believe them. Rather than try and explain all these
entries lets start with some examples.</p>
<p>An <strong>objectClass</strong> is defined using <a title="" href="http://www.zytrax.com/books/ldap/apd/index.html#asn1" class="t-db">ASN.1</a> notation - the following is a simple standard objectclass definition for <a title="" href="http://www.zytrax.com/books/ldap/ape/core-schema.html#country" class="t-db">country</a> taken from the <a title="" href="http://www.zytrax.com/books/ldap/ape/core-schema.html" class="t-db">core.schema</a> supplied with OpenLDAP distributions.</p>
<pre>objectclass ( 2.5.6.2 NAME 'country' SUP top STRUCTURAL<br />
MUST c<br />
MAY ( searchGuide $ description ) )<br />
</pre>
<p>Now lets deconstruct this definition:</p>
<p><strong>objectclass</strong> is a keyword indicating this is an objectclass definition - see it's not so complicated!</p>
<p><strong>2.5.6.2 NAME 'country'</strong> defines a <strong>globally unique</strong> name for this objectclass and is comprised of two parts: <strong>NAME 'country'</strong> just allows you to refer to this objectclass by some semi-understandable text - in this case the english word <strong>country</strong>. The <strong>globally unique</strong> part is defined by <strong>2.5.6.2</strong> which is called an <a title="" href="http://www.zytrax.com/books/ldap/apa/oid.html" class="t-db">OID (ObjectIdentifier)</a>.
The OID 2.5.6.2 was probably the third objectclass ever defined by
X.500 (2.5.6 is the joint itu-iso x.500 object classes, the last 2 is a
sequence number within that family of OIDs). It does not matter what
organization assigns this number but it must be UNIQUE. Obtaining an
enterprise OID that allows you to define your own <strong>attributes</strong> and <strong>objectclasses</strong> is a trivial and zero cost process via <a href="http://pen.iana.org/pen/PenApplication.page" class="t-db">IANA</a>. It is a VERY BAD THING&#8482; to re-use existing OIDs.</p>
<p><strong>SUP 'top'</strong> indicates that this objectclass has a PARENT (or
SUPerior) objectclass - it is part of a hierarchy. In this case the
parent is <strong>top</strong> which is a special class that terminates (is the
highest level) in all objectclasses. An objectclass may have one or
more objectclass(es) as Parents.</p>
<p><strong>STRUCTURAL</strong> indicates that this objectclass contains data and can form an <a href="http://www.zytrax.com/books/ldap/apd/index.html#entry" class="t-db">entry</a> in a DIT. <strong>objectClasses</strong> may also be ABSTRACT which indicates a non-existent objectclass used for convenience. The most common ABSTRACT objectclass is <strong>top</strong> which just terminates an objectclass hierarchy. Finally an <strong>objectClass</strong>
may be AUXILIARY which indicates it may be used with any STRUCTURAL
objectclass to form an entry but cannot alone form an entry in a DIT.</p>
<p><strong>DESC 'description'</strong> OK so we picked a lousy example which does
not have a DESC part - but it was short. DESC is an optional value that
provides a short text description of the use or contents of the
objectclass. It's meant for human beings to read and has no other use.
Here is what <strong>country</strong> <u>could</u> have looked like with a DESC statement included:</p>
<pre>objectclass ( 2.5.6.2 NAME 'country' SUP top STRUCTURAL<br />
DESC '2 character iso assigned country code'<br />
MUST c<br />
MAY ( searchGuide $ description ) )<br />
</pre>
<p><strong>MUST c</strong> MUST indicates that the attributes in the following list are mandatory in this case the attribute <strong>c</strong>
has to be present or the entry will fail to load. Single values are
written as shown, multiple attributes are enclosed in parentheses and
separated with a $ (dollar) sign, such as ( attr1 $ attr2 $ attrn). If
there are no mandatory attributes this section is not included.</p>
<p><strong>MAY ( searchGuide $ description )</strong> MAY indicates that the
attributes in the following list are optional. Multiple values are
written as shown, single attributes do not need the parentheses (see
above). If there are no optional attributes this section is not
included.</p>
<h3>Some more objectClasses</h3>
<p>This is how the <strong>top</strong> objectclass is defined:</p>
<pre>objectclass ( 2.5.6.0 NAME 'top' ABSTRACT<br />
MUST objectClass )<br />
</pre>
<p>Illustrates the use of the ABSTRACT statement in an objectclass. Since <strong>top</strong> is always the top of a hierarchy - clearly it cannot have a <strong>SUP</strong> statement. The OID is also assigned by the X.500 standards group.</p>
<p>Many documents insist that the objectclass <strong>top</strong> is included in <a href="http://www.zytrax.com/books/ldap/apd/index.html#ldif" class="t-db">LDIF</a> files - <a href="http://www.zytrax.com/books/ldap/apa/ldif-objects.html" class="t-db">it is not always necessary</a>.</p>
<p>This is how the <strong>dcObject</strong> objectclass is defined:</p>
<pre>objectclass ( 1.3.6.1.4.1.1466.344 NAME 'dcObject'<br />
DESC 'RFC2247: domain component object'<br />
SUP top AUXILIARY MUST dc )<br />
</pre>
<p>Illustrates the use of the AUXILLIARY statement. An AUXILLIARY
cannot on its own create an entry. The OID in this example shows the
use of a <a title="" href="http://www.zytrax.com/books/ldap/apa/oid.html" class="t-db">private enterprise OID (ObjectIdentifier)</a>. The following fragment shows a fairly typical base DN definition using <strong>dcObject</strong>:</p>
<pre>dn: dc=example,dc=com<br />
dc: example.com<br />
objectclass: dcObject<br />
objectclass: organization<br />
o: Example, Inc.<br />
</pre>
<p>It is the <strong>objectclass: organization</strong> that creates the entry. <strong>dcObject</strong> piggy-backs on this objectclass.</p>
<p>This is how the <strong>pilotOrganization</strong> objectclass is defined and
illustrates that there may be one or more SUPerior (Parent)
objectclasses in which the child inherits the properties of ALL its
parents (bit like humans really):</p>
<pre>objectClasses: ( 0.9.2342.19200300.100.4.20 NAME 'pilotOrganization'<br />
SUP ( organization $ organizationalUnit ) STRUCTURAL<br />
MAY buildingName )<br />
</pre>
<p>We have omitted explaining a couple of values (well one actually) -
OBSOLETE if it is present it means the objectclass should not be used
(duh).</p>
<h2>LDAP Attributes</h2>
<p>Attributes typically contain data and have the following characteristics:</p>
<ol>
    <li>
    <p>Every <strong>attribute</strong> is included in one or more <strong>objectclass</strong>.</p>
    </li>
    <li>
    <p>An <strong>objectclass</strong> is also an <strong>attribute</strong> and can be used in searches.</p>
    </li>
    <li>
    <p>To use an <strong>attribute</strong> in an <a title="" href="http://www.zytrax.com/books/ldap/apd/index.html#entry" class="t-db">entry</a> its <strong>objectclass</strong> must be included in the entry definition and its <strong>objectclass</strong> must be included in a <strong>schema</strong> which must be identified to the LDAP server.</p>
    </li>
    <li>
    <p>An <strong>attribute</strong>'s characteristics are defined using <a title="" href="http://www.zytrax.com/books/ldap/apd/index.html#asn1" class="t-db">ASN.1</a> notation.</p>
    </li>
    <li>
    <p>An <strong>attribute</strong> can appear once in any instance of its containing <strong>ObjectClass</strong> (SINGLE-VALUE) or can apear more than once in any instance of its containing <strong>ObjectClass</strong> (MULTI-VALUE). MULTI-VALUE is default.</p>
    </li>
    <li>
    <p>An <strong>attribute</strong> definition may be part of a hierarchy in
    which case it inherits all the properties of its parents, for example,
    commonName (cn), givenName (gn), surname (sn) are all children of the <strong>name</strong> attribute.</p>
    </li>
    <li>
    <p>An <strong>attribute</strong> definition includes its type, for instance
    string, number etc., how it behaves in certain conditions, for example
    are compares case sensitive or case-insensitive and other
    characteristics (properties).</p>
    </li>
    <li>
    <p>An <strong>attribute</strong> supported by a LDAP server forms part of a <strong>collection</strong> called <strong>attributetypes</strong> which can be interrogated via the <a title="" href="http://www.zytrax.com/books/ldap/ch3/#operational" class="t-db">subschema</a>.</p>
    </li>
</ol>
<h3>Defining an Attribute</h3>
<p>The formal attribute definition is defined in <a href="http://www.zytrax.com/books/ldap/apc/rfc2252.txt" class="t-db">RFC 2252 section 4.2</a> and looks like this:</p>
<pre>AttributeTypeDescription = "(" whsp<br />
numericoid whsp     ; AttributeType identifier<br />
[ "NAME" qdescrs ]             ; name used in AttributeType<br />
[ "DESC" qdstring ]            ; description<br />
[ "OBSOLETE" whsp ]<br />
[ "SUP" woid ]                 ; derived from this other<br />
; AttributeType<br />
[ "EQUALITY" woid              ; Matching Rule name<br />
[ "ORDERING" woid              ; Matching Rule name<br />
[ "SUBSTR" woid ]              ; Matching Rule name<br />
[ "SYNTAX" whsp noidlen whsp ] ; Syntax OID<br />
[ "SINGLE-VALUE" whsp ]        ; default multi-valued<br />
[ "COLLECTIVE" whsp ]          ; default not collective<br />
[ "NO-USER-MODIFICATION" whsp ]; default user modifiable<br />
[ "USAGE" whsp AttributeUsage ]; default userApplications<br />
whsp ")"<br />
</pre>
<p>Ouch! <strong>whsp</strong> means a space character and must be present. Rather than explain each bit of gobbledegook lets again start with some examples.</p>
<p>An <strong>attribute</strong> is defined using <a title="" href="http://www.zytrax.com/books/ldap/apd/index.html#asn1" class="t-db">ASN.1</a> notation - the following is a simple standard attribute definition for <a title="" href="http://www.zytrax.com/books/ldap/ape/core-schema.html#commonname" class="t-db">commonName (cn)</a> taken from the <a title="" href="http://www.zytrax.com/books/ldap/ape/core-schema.html" class="t-db">core.schema</a> supplied with OpenLDAP distributions.</p>
<pre>attributetype ( 2.5.4.3 NAME ( 'cn' 'commonName' ) SUP name )<br />
</pre>
<p>Now lets deconstruct this definition:</p>
<p><strong>attributetype</strong> indicates this defines an attribute - wow.</p>
<p><strong>2.5.4.3 NAME ('cn' 'commonName')</strong> defines a <strong>globally unique</strong> name for this attribute and is comprised of two parts: <strong>NAME ('cn' 'commonName'</strong> just allows you to refer to this attribute by some semi-understandable text - in this case either the english word <strong>commonName</strong> OR the shortform (or alias) <strong>cn</strong>
in principle there are no limits to the number of definitions or
aliases you can have as long as they are unique. In this multiple entry
form the names are enclosed in parentheses and space separated. Since <strong>cn</strong> appears first it is called the <a href="http://www.zytrax.com/books/ldap/apd/index.html#primary" class="t-db">primary</a> name which is very important when it comes to <a href="http://www.zytrax.com/books/ldap/apa/indeces.html" class="t-db">indexing</a> entries.</p>
<p>The <strong>globally unique</strong> part is defined by <strong>2.5.4.3</strong> which is called an <a title="" href="http://www.zytrax.com/books/ldap/apa/oid.html" class="t-db">OID (ObjectIdentifier)</a>.
The OID 2.5.4.3 was possibly the fourth attribute ever defined by X.500
(2.5.4 is the joint itu-iso x.500 attribute types, the last 3 is a
sequence number within that family of OIDs). It does not matter what
organization assigns this number but it must be UNIQUE. Obtaining an
enterprise OID that allows you to define your own <strong>attributes</strong> and <strong>objectclasses</strong> is a trivial process via <a href="http://pen.iana.org/pen/PenApplication.page" class="t-db">IANA</a>. It is a VERY BAD THING&#8482; to re-use existing OIDs.</p>
<p><strong>SUP 'name'</strong> indicates that this attribute has a PARENT (or SUPerior) attribute - it is part of a hierarchy. In this case the parent is <strong>name</strong>
which we will now look at in detail since, if you recall, the child
always inherits the properties of the parent (or SUPerior) attribute
(and itself may have additional properties). The SUP entry can use
either a 'name' or an OID. The definition SUP 'top' and SUP 2.5.4.41
mean exactly the same - except to the poor reader!</p>
<p>This is the attribute definition of <a title="" href="http://www.zytrax.com/books/ldap/ape/core-schema.html#name" class="t-db">name</a> which is a much more serious definition and the SUPerior (parent) attribute of <strong>cn</strong> above:</p>
<pre>attributetype ( 2.5.4.41 NAME 'name'<br />
EQUALITY caseIgnoreMatch<br />
SUBSTR caseIgnoreSubstringsMatch<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )<br />
</pre>
<p>Now for some more serious deconstruction:</p>
<p><strong>attributetype</strong> indicates this defines an attribute - same as before.</p>
<p><strong>2.5.4.41 NAME 'name'</strong> defines the <strong>globally unique</strong> name for this attribute and as before is comprised of two parts: <strong>NAME 'name'</strong> just allows reference to this attribute by some semi-understandable text and the OID <strong>2.5.4.41</strong>
indicates it was defined by the X.500 standards group. The format used,
because there is only a single name value, does not need enclosing
parentheses as in the <strong>commonName</strong> example above.</p>
<p><strong>EQUALITY caseIgnoreMatch</strong> indicates how this (and any child attributes) will behave when used in a <a href="http://www.zytrax.com/books/ldap/apa/search.html" class="t-db">search filter</a> e.g. <strong>(cn=jimbob)</strong> (<strong>cn</strong> is a child of <strong>name</strong>) and no <strong>wildcards</strong> exist in the search. In this case it defines the match to be case-insensitive. <strong>caseIgnoreMatch</strong> is a <a href="http://www.zytrax.com/books/ldap/ch3/#matchingrules" class="t-db">matchingRule</a> and is defined in the <a href="http://www.zytrax.com/books/ldap/ch3/#operational" class="t-db">subschema</a>.</p>
<p><strong>SUBSTR caseIgnoreSubstringsMatch</strong> indicates how this (and any child attributes) will behave when used in a <a href="http://www.zytrax.com/books/ldap/apa/search.html" class="t-db">search filter</a> which uses a substring e.g. <strong>(cn=jim*)</strong> (<strong>cn</strong> is a child of <strong>name</strong>) and contains one or more <strong>wildcards</strong>. In this case it defines that the match is case-insensitive. <strong>caseIgnoreSubstringMatch</strong> is again a <a href="http://www.zytrax.com/books/ldap/ch3/#matchingrules" class="t-db">matchingRule</a> and is defined in the <a href="http://www.zytrax.com/books/ldap/ch3/#operational" class="t-db">subschema</a>.</p>
<p><strong>SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768}</strong> is an <a href="http://www.zytrax.com/books/ldap/apa/oid.html" class="t-db">OID</a> which defines the data type and what rules (data validation) are applied to the data. The full list is in <a href="http://www.zytrax.com/books/ldap/apc/rfc2252.txt" class="t-db">RFC 2252 section 4.3.2</a> and in this case the OID defines it to be a Directory String type which is defined in <a href="http://www.zytrax.com/books/ldap/apc/rfc2252.txt" class="t-db">RFC 2252 section 6.10</a> to be in the UTF-8 form of the ISO 10646 character set. The value <strong>{32768}</strong> indicates the maximum length of the string and is optional. <a href="http://www.zytrax.com/books/ldap/apa/types.html" class="t-db">Some more on LDAP Data Types</a></p>
<h2>Other Characteristics</h2>
<p><strong>SINGLE-VALUE</strong> <u>Omission</u> of this entry means that it is multi-valued i.e. it can appear more than once in an <strong>objectclass</strong> or an entry. If the attribute can only accept single values it must be explicitly defined as in the definition of <strong>dc</strong> below.</p>
<pre>attributetype ( 0.9.2342.19200300.100.1.25<br />
NAME ( 'dc' 'domainComponent' )<br />
DESC 'RFC1274/2247: domain component'<br />
EQUALITY caseIgnoreIA5Match<br />
SUBSTR caseIgnoreIA5SubstringsMatch<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 <br />
SINGLE-VALUE )<br />
</pre>
<p><strong>ORDERING 'matchingrule'</strong> is rarely defined and is used to
define the collation match - the lexicographic sorting order (allowing
searches of &lt;= and &gt;=).</p>
<pre>attributetype ( 2.5.4.46 NAME 'dnQualifier'<br />
EQUALITY caseIgnoreMatch<br />
ORDERING caseIgnoreOrderingMatch<br />
SUBSTR caseIgnoreSubstringsMatch<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )<br />
</pre>
<p><a href="http://www.zytrax.com/books/ldap/ch3/#contents"><img src="http://www.zytrax.com/images/go_up.gif" alt="Up Arrow" border="0" /></a></p>
<h2>3.5 Matching Rules</h2>
<p>Matching rules are part of what is called the <a title="" href="http://www.zytrax.com/books/ldap/ch3/#operational" class="t-db">operational</a> characteristics of the LDAP server.</p>
<div>
<p><strong>matchingrules</strong> define the methods of comparison available in the LDAP server:</p>
<ol>
    <li><strong>matchingrules</strong> are typically built-in to the LDAP server and do not need to be defined explicitly.</li>
    <li>A <strong>matchingrule</strong> forms part of a <strong>collection</strong> called <strong>matchingrules</strong> which can be discovered via the <a title="" href="http://www.zytrax.com/books/ldap/ch3/#operational" class="t-db">subschema</a>.</li>
    <li>A <strong>matchingrule</strong> is defined for each <strong>attribute</strong> using the <a title="" href="http://www.zytrax.com/books/ldap/ch3/#attribute-def" class="t-db">EQUALITY, SUBSTR, ORDERING</a> properties as required - only those properties required are defined. If the search cannot use a <strong>wildcard</strong> there will be no SUBSTR property defined.</li>
</ol>
<h3>3.5.1 Defining matchingRule</h3>
<p>Most <strong>matchingrules</strong> are built-in and you almost never need to
define them but like everything in LDAP it has a defining syntax. The
following is an example of a matchingrule definition using <strong>caseIgnoreMatch</strong>:</p>
<pre>matchingRule ( 2.5.13.2 NAME 'caseIgnoreMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )<br />
</pre>
<p>The deconstruction shows the following:</p>
<p><strong>matchingrule</strong> indicates this is a matchingrule definition.</p>
<p><strong> 2.5.13.2 NAME 'caseIgnoreMatch'</strong> defines the <strong>globally unique</strong> name for this matching rule and as always is comprised of two parts: <strong>NAME 'caseIgnoreMatch'</strong> allows reference to this matchingrule using some semi-understandable text and the OID <strong>2.5.13.2</strong> indicates the matching rule was defined by the X.500 standards group. Rule description:</p>
<div>
<p>"The Case Ignore Match rule compares for equality a presented string
with an attibute value of type PrintableString, NumericString,
TeletexString, BMPString, UniversalString or DirectoryString without
regard for case (upper or lower) of the strings (e.g., "Dundee" and
"DUNDEE" match).</p>
<p>The rule returns TRUE if the strings are the same length and
corresponding characters are identical except possibly with regard to
case. </p>
</div>
<p><strong>SYNTAX 1.3.6.1.4.1.1466.115.121.1.15</strong> defines that this matchingrule operates on the type(s) defined - in this case a DirectoryString (a UTF-8 format string).</p>
<h3>OpenLDAP built-in matchingRules</h3>
<p>This list below can be found for OpenLDAP by interrogating the <strong>subschema</strong> using a command like:</p>
<pre>ldapsearch -H ldap://ldap.example.com -x -s base -b "cn=subschema"<br />
"(objectclass=*)" matchingrules<br />
# matchingrules may be changed to <br />
# attributetypes objectclasses etc., etc.<br />
</pre>
<p>The above command should be on a single line - it is split for HTML
formatting reasons only. Replace ldap.example.com with the host name of
your LDAP server. If the server is running locally you can omit the -H
argument.</p>
<p>Alternatively use any good LDAP browser with a Root DN of "cn=subschema"</p>
<p>The above command will return this list (OpenLDAP 2.1.12 on FreeBSD):</p>
<pre># Subschema<br />
dn: cn=Subschema<br />
matchingRules: ( 2.5.13.0 NAME 'objectIdentifierMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )<br />
matchingRules: ( 2.5.13.1 NAME 'distinguishedNameMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )<br />
matchingRules: ( 2.5.13.2 NAME 'caseIgnoreMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )<br />
matchingRules: ( 2.5.13.3 NAME 'caseIgnoreOrderingMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )<br />
matchingRules: ( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )<br />
matchingRules: ( 2.5.13.5 NAME 'caseExactMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )<br />
matchingRules: ( 2.5.13.6 NAME 'caseExactOrderingMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )<br />
matchingRules: ( 2.5.13.7 NAME 'caseExactSubstringsMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )<br />
matchingRules: ( 2.5.13.8 NAME 'numericStringMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )<br />
matchingRules: ( 2.5.13.10 NAME 'numericStringSubstringsMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )<br />
matchingRules: ( 2.5.13.13 NAME 'booleanMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )<br />
matchingRules: ( 2.5.13.14 NAME 'integerMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )<br />
matchingRules: ( 2.5.13.15 NAME 'integerOrderingMatch' <br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )<br />
matchingRules: ( 2.5.13.16 NAME 'bitStringMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )<br />
matchingRules: ( 2.5.13.17 NAME 'octetStringMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )<br />
matchingRules: ( 2.5.13.18 NAME 'octetStringOrderingMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )<br />
matchingRules: ( 2.5.13.20 NAME 'telephoneNumberMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )<br />
matchingRules: ( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )<br />
matchingRules: ( 2.5.13.23 NAME 'uniqueMemberMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )<br />
matchingRules: ( 2.5.13.27 NAME 'generalizedTimeMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )<br />
matchingRules: ( 2.5.13.28 NAME 'generalizedTimeOrderingMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )<br />
matchingRules: ( 2.5.13.29 NAME 'integerFirstComponentMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )<br />
matchingRules: ( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )<br />
matchingRules: ( 2.5.13.34 NAME 'certificateExactMatch'<br />
SYNTAX 1.2.826.0.1.3344810.7.1 )<br />
matchingRules: ( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )<br />
matchingRules: ( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )<br />
matchingRules: ( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )<br />
matchingRules: ( 1.3.6.1.4.1.4203.1.2.1 NAME 'caseExactIA5SubstringsMatch' <br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )<br />
matchingRules: ( 1.2.840.113556.1.4.803 NAME 'integerBitAndMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )<br />
matchingRules: ( 1.2.840.113556.1.4.804 NAME 'integerBitOrMatch'<br />
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )<br />
</pre>
<p>You can find what the <strong>OIDs</strong> are and therefore the exact english description of the matchingRule using this <a title="" href="http://www.alvestrand.no/objectid/top.html" class="t-db">wonderful site</a>.</p>
<p><a href="http://www.zytrax.com/books/ldap/ch3/#contents"><img src="http://www.zytrax.com/images/go_up.gif" alt="Up Arrow" border="0" /></a></p>
<h2>3.6 LDAP Operational Attributes and Objects</h2>
<p>There are a bunch of attributes and objectclasses that are built
into the LDAP server and govern how it works or functions. These
attributes and object classes are typically called <strong>operational</strong>.</p>
<p>These <strong>operational thingies</strong> all live under the <a href="http://www.zytrax.com/books/ldap/apd/index.html#rootdse" class="t-db">rootDSE</a> and are not visible in normal operations.</p>
<p>The relationship between the DIT(s) and its entries and the RootDSE and its objects is shown below:</p>
<p align="center"><img title="" src="http://www.zytrax.com/books/ldap/images/ldap-rootdse.gif" alt="" border="0" /></p>
<p>The rootDSE can be inspected using either a suitable LDAP browser (instructions for <a href="http://www.zytrax.com/books/ldap/ch14/ldapbrowser.html#operational" class="t-db">LDAPBrowser/Editor</a>) with an empty Root DN or the following command:</p>
<pre>ldapsearch -H ldap://ldap.mydomain.com -x -s base -b "" +<br />
# note the + returns operational attributes<br />
</pre>
<p>This should return something similar to that shown below (from
OpenLDAP 2.4.8) - the values in parentheses are added explanations and
are not returned by the server:</p>
<pre>dn:<br />
structuralObjectClass: OpenLDAProotDSE<br />
configContext: cn=config<br />
namingContexts: dc=example,dc=com<br />
namingContexts: dc=example,dc=net<br />
monitorContext: cn=Monitor<br />
supportedControl: 1.3.6.1.4.1.4203.1.9.1.1 (Contentsync <a href="http://www.zytrax.com/books/ldap/apc/rfc4530.txt" class="t-db">RFC 4530</a>)<br />
supportedControl: 2.16.840.1.113730.3.4.18 (ProxiedAuthv2 <a href="http://www.zytrax.com/books/ldap/apc/rfc4370.txt" class="t-db">RFC 4370</a>)<br />
supportedControl: 2.16.840.1.113730.3.4.2 (ManageDSAIT <a href="http://www.zytrax.com/books/ldap/apc/rfc3377.txt" class="t-db">RFC3377</a>)<br />
supportedControl: 1.3.6.1.4.1.4203.1.10.1 (SubEntries <a href="http://www.zytrax.com/books/ldap/apc/rfc3673.txt" class="t-db">RFC3673</a>)<br />
supportedControl: 1.2.840.113556.1.4.319 (pagedResults <a href="http://www.zytrax.com/books/ldap/apc/rfc2696.txt" class="t-db">RFC2696</a>)<br />
supportedControl: 1.2.826.0.1.3344810.2.3 (MatchedValues <a href="http://www.zytrax.com/books/ldap/apc/rfc3876.txt" class="t-db">RFC3876</a>)<br />
supportedControl: 1.3.6.1.1.13.2 (Post Read <a href="http://www.zytrax.com/books/ldap/apc/rfc4527.txt" class="t-db">RFC4527</a>)<br />
supportedControl: 1.3.6.1.1.13.1 (Pre-Read <a href="http://www.zytrax.com/books/ldap/apc/rfc4527.txt" class="t-db">RFC4527</a>))<br />
supportedControl: 1.3.6.1.1.12 (Assertion <a href="http://www.zytrax.com/books/ldap/apc/rfc4528.txt" class="t-db">RFC4528</a>)<br />
supportedExtension: 1.3.6.1.4.1.4203.1.11.1 (ModifyPassword <a href="http://www.zytrax.com/books/ldap/apc/rfc3088.txt" class="t-db">RFC3088</a>)<br />
supportedExtension: 1.3.6.1.4.1.4203.1.11.3 (WhoAmI <a href="http://www.zytrax.com/books/ldap/apc/rfc4532.txt" class="t-db">RFC4532</a>)<br />
supportedExtension: 1.3.6.1.1.8 (Cancel <a href="http://www.zytrax.com/books/ldap/apc/rfc3909.txt" class="t-db">RFC3909</a>)<br />
supportedFeatures: 1.3.6.1.1.14 (Modify-Increment <a href="http://www.zytrax.com/books/ldap/apc/rfc4525.txt" class="t-db">RFC4525</a>)<br />
supportedFeatures: 1.3.6.1.4.1.4203.1.5.1 (OperationalAttrs <a href="http://www.zytrax.com/books/ldap/apc/rfc3674.txt" class="t-db">RFC3674</a>)<br />
supportedFeatures: 1.3.6.1.4.1.4203.1.5.2 (ObjectClassAttrs <a href="http://www.zytrax.com/books/ldap/apc/rfc4529.txt" class="t-db">RFC4529</a>)<br />
supportedFeatures: 1.3.6.1.4.1.4203.1.5.3 (TrueFalse <a href="http://www.zytrax.com/books/ldap/apc/rfc4526.txt" class="t-db">RFC4526</a>)<br />
supportedFeatures: 1.3.6.1.4.1.4203.1.5.4 (LanguageTag <a href="http://www.zytrax.com/books/ldap/apc/rfc3866.txt" class="t-db">RFC3866</a>)<br />
supportedFeatures: 1.3.6.1.4.1.4203.1.5.5 (LanguageRange <a href="http://www.zytrax.com/books/ldap/apc/rfc3866.txt" class="t-db">RFC3866</a>)<br />
supportedLDAPVersion: 3<br />
supportedSASLMechanisms: NTLM<br />
supportedSASLMechanisms: GSSAPI<br />
supportedSASLMechanisms: DIGEST-MD5<br />
supportedSASLMechanisms: CRAM-MD5<br />
entryDN:<br />
subschemaSubentry: cn=Subschema<br />
</pre>
<p>An explanation of each <strong>supportedExtension</strong> can be found using this <a title="" href="http://www.alvestrand.no/objectid/top.html" class="t-db">wonderful site</a>. The above listing shows this LDAP server supports two <a title="" href="http://www.zytrax.com/books/ldap/apd/index.html#dit" class="t-db">DITs</a> - shown as <strong>namingContexts</strong> - <a title="" href="http://www.zytrax.com/books/ldap/ch11/multi-dit.html" class="t-db">which were configured using this process</a>.</p>
<p>It is possible to add extensions using the OpenLDAP slapd.conf <a title="" href="http://www.zytrax.com/books/ldap/ch6/index.html#rootdse" class="t-db">rootDSE</a> directive.</p>
<p><br />
</p>
<p>参见URL：</p>
<p>http://www.zytrax.com/books/ldap/ch3/</p>
</div>
<img src ="http://www.blogjava.net/liuzheng/aggbug/267316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2009-04-24 11:07 <a href="http://www.blogjava.net/liuzheng/articles/267316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ldap ldap-schema</title><link>http://www.blogjava.net/liuzheng/articles/267314.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 24 Apr 2009 03:00:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/267314.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/267314.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/267314.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/267314.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/267314.html</trackback:ping><description><![CDATA[URL:<br />
http://www.it.ufl.edu/projects/directory/ldap-schema/<br />
<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/267314.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2009-04-24 11:00 <a href="http://www.blogjava.net/liuzheng/articles/267314.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用MyEclipse +Tomcat进行web project开发</title><link>http://www.blogjava.net/liuzheng/articles/248055.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Wed, 24 Dec 2008 05:50:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/248055.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/248055.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/248055.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/248055.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/248055.html</trackback:ping><description><![CDATA[1.add web project into the MyEclipse<br />
2.add web capabilities to the web project , 设置web Root Directory 到 src/main/webapp<br />
3.设置output路径到webapp/classes下<br />
<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/248055.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-12-24 13:50 <a href="http://www.blogjava.net/liuzheng/articles/248055.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Eclipse中设置sub projects的联动调试</title><link>http://www.blogjava.net/liuzheng/articles/247917.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 23 Dec 2008 07:12:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/247917.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/247917.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/247917.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/247917.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/247917.html</trackback:ping><description><![CDATA[可以在Eclipse中设置sub projects的联动调试.<br />
如果有个sub project A 和B 他们是相关联的，A是后台主件，B在容器中跑，那么可以设置关联，使其修改A后能够在容器中B看到显示。<br />
<img src="file:///C:/DOCUME%7E1/jack.liu/LOCALS%7E1/Temp/moz-screenshot.jpg" alt="" />在Java Build Path 中的projects Add A就可以了<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/247917.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-12-23 15:12 <a href="http://www.blogjava.net/liuzheng/articles/247917.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>很好的一个展示table的标签</title><link>http://www.blogjava.net/liuzheng/articles/247754.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Mon, 22 Dec 2008 06:24:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/247754.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/247754.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/247754.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/247754.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/247754.html</trackback:ping><description><![CDATA[display table<br />
文档如下：<br />
<br />
http://displaytag.sourceforge.net/10/
<img src ="http://www.blogjava.net/liuzheng/aggbug/247754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-12-22 14:24 <a href="http://www.blogjava.net/liuzheng/articles/247754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Dom4j和XPath解析spring 的配置文件</title><link>http://www.blogjava.net/liuzheng/articles/246656.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 16 Dec 2008 08:27:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/246656.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/246656.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/246656.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/246656.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/246656.html</trackback:ping><description><![CDATA[&nbsp;Map m = new HashMap&lt;String, String&gt;();<br />
&nbsp;m.put("s", "http://www.springframework.org/schema/beans");<br />
&nbsp;XPath xpathSelector = DocumentHelper.createXPath("//s:ref");<br />
&nbsp;xpathSelector.setNamespaceURIs(m);<br />
List&lt;Node&gt; list = xpathSelector.selectNodes(document);<br />
<br />
<br />
注意对于使用schema进行验证的spring configuration<br />
<br />
要在XPath上加入域名空间。 xpathSelector.setNamespaceURIs(m);
<img src ="http://www.blogjava.net/liuzheng/aggbug/246656.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-12-16 16:27 <a href="http://www.blogjava.net/liuzheng/articles/246656.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Aspectj学习网站</title><link>http://www.blogjava.net/liuzheng/articles/245648.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Thu, 11 Dec 2008 03:32:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/245648.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/245648.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/245648.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/245648.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/245648.html</trackback:ping><description><![CDATA[http://www.eclipse.org/aspectj/doc/released/adk15notebook/index.html
<img src ="http://www.blogjava.net/liuzheng/aggbug/245648.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-12-11 11:32 <a href="http://www.blogjava.net/liuzheng/articles/245648.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GWT国际化的处理</title><link>http://www.blogjava.net/liuzheng/articles/243296.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 28 Nov 2008 09:11:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/243296.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/243296.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/243296.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/243296.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/243296.html</trackback:ping><description><![CDATA[GWT的国际化需要做到以下几点：<br />
1.使用GWT初始化Constants<br />
final MCenterConstants constants = (MCenterConstants) GWT.create(MCenterConstants.class);<br />
2.改变URL加入locale<br />
&nbsp; public void onClick(Widget sender) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Window.open(GWT.getHostPageBaseURL() + "?locale=" + "en", "_self", "");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/243296.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-11-28 17:11 <a href="http://www.blogjava.net/liuzheng/articles/243296.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AspectJ的学习网址</title><link>http://www.blogjava.net/liuzheng/articles/243294.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 28 Nov 2008 09:07:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/243294.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/243294.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/243294.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/243294.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/243294.html</trackback:ping><description><![CDATA[http://www.eclipse.org/aspectj/doc/released/progguide/starting-aspectj.html
<img src ="http://www.blogjava.net/liuzheng/aggbug/243294.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-11-28 17:07 <a href="http://www.blogjava.net/liuzheng/articles/243294.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi验证授权的数据库设计</title><link>http://www.blogjava.net/liuzheng/articles/231338.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 26 Sep 2008 09:24:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/231338.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/231338.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/231338.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/231338.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/231338.html</trackback:ping><description><![CDATA[<pre>CREATE DATABASE acegi;<br />
<br />
USE acegi;<br />
<br />
<br />
<br />
CREATE TABLE USERS(  <br />
<br />
USERNAME VARCHAR(50) NOT NULL PRIMARY KEY,  <br />
<br />
PASSWORD VARCHAR(50) NOT NULL,  <br />
<br />
ENABLED BIT NOT NULL<br />
<br />
);<br />
<br />
<br />
<br />
INSERT INTO USERS(username,password,enabled) values('caterpillar' ,'123456', 1);<br />
<br />
INSERT INTO USERS(username,password,enabled) values('user1' ,'user1pwd', 1);<br />
<br />
INSERT INTO USERS(username,password,enabled) values('user2' ,'user2pwd', 0);<br />
<br />
<br />
<br />
CREATE TABLE AUTHORITIES(  <br />
<br />
USERNAME VARCHAR( 50 ) NOT NULL,  <br />
<br />
AUTHORITY VARCHAR( 50 ) NOT NULL,  <br />
<br />
CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME)  <br />
<br />
);  <br />
<br />
<br />
<br />
INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( 'caterpillar' , 'ROLE_SUPERVISOR');<br />
<br />
INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( 'user1', 'ROLE_USER');  <br />
<br />
INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( 'user2', 'ROLE_USER');</pre>
<img src ="http://www.blogjava.net/liuzheng/aggbug/231338.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-09-26 17:24 <a href="http://www.blogjava.net/liuzheng/articles/231338.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从业务流程中取出Acegi的SecurityContext</title><link>http://www.blogjava.net/liuzheng/articles/231333.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 26 Sep 2008 09:06:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/231333.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/231333.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/231333.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/231333.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/231333.html</trackback:ping><description><![CDATA[<br />
SecurityContext context = SecurityContextHolder.getContext();<br />
就可以得到了context<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/231333.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-09-26 17:06 <a href="http://www.blogjava.net/liuzheng/articles/231333.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi架构</title><link>http://www.blogjava.net/liuzheng/articles/231331.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 26 Sep 2008 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/231331.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/231331.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/231331.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/231331.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/231331.html</trackback:ping><description><![CDATA[<br />
下图为Acegi的架构流程简图：<br />
<img style="width: 533px; height: 266px;" alt="" src="http://caterpillar.onlyfun.net/GossipCN/AcegiGossip/images/AcegiArchitecture-1.jpg" /><br />
<br />
<br />
<ol>
    <li>浏览器发出请求。</li>
    <li>容器将请求包装为请求物件与回应物件，然后呼叫Acegi的Filter Chain Proxy，将请求物件与回应物件传递给它。</li>
    <li>Filter Chain Proxy呼叫Filter Chain的第一个Filter，通常是Session Integration Filter，将请求物件与回应物件传递给它。</li>
    <li>Session Integration Filter检查Session物件是否存在，并且包括Security
    Context物件，如果否则建立Security Context物件并将之放入security context
    holder之中，一个存在于application scope的物件。</li>
    <li>每个Filter完成后会呼叫下一个Filter。</li>
    <li>每
    个Filter会读取或变更Security Context，每个Filter处理完成之后，最后控制权再度回到Session
    Integration Filter后，Session Integration Filter检查Security
    Context是否有变更，如果有变更，使用Security Context中的资讯更新Session物件。</li>
    <li>完成Filter Chain，控制权进入应用程式，开始处理请求并准备回应。</li>
    <li>将结果回应传回至浏览器。</li>
</ol>
<img src ="http://www.blogjava.net/liuzheng/aggbug/231331.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-09-26 17:01 <a href="http://www.blogjava.net/liuzheng/articles/231331.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi系统元件</title><link>http://www.blogjava.net/liuzheng/articles/231330.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 26 Sep 2008 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/231330.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/231330.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/231330.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/231330.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/231330.html</trackback:ping><description><![CDATA[入门Acegi最重要的是了解基本架构，如此就不会被一堆设定档案搞的混头转向，从先前&#8220;第一个Acegi程式&#8221;，可以看到Acegi系统包括的四个主要类型元件：Filter、Manager、Provider与Handler。<br />
<ul>
    <li>Filter</li>
</ul>
<div style="margin-left: 40px;">当一个请求到来时，在安全处理上最高层的元件，像是会话处理、验证、登出等，并呼叫对应的物件进行处理。</div>
<ul>
    <li>Manager</li>
</ul>
<div style="margin-left: 40px;">真正处理验证、登出等安全服务之元件，Manager管理Provider所提供的安全相关资讯。</div>
<ul>
    <li>Provider</li>
</ul>
<div style="margin-left: 40px;">提供安全相关资讯给Manager，安全资讯来源可能是记忆体中的物件、档案、资料库等储存媒介，安全资讯包括了使用者名称、密码、角色等讯息。</div>
<ul>
    <li>Handler</li>
</ul>
<div style="margin-left: 40px;">有时会将一个安全服务分作数个小任务来进行，每个小任务由一个Handler来进行处理，如此在处理安全服务或设定Acegi时可以更有弹性，例如依需求处理登出时Session的失效与Cookie的失效。</div>
<br />
Acegi使用Filter来对请求进行验证与授权等安全服务：<br />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp; &nbsp; &lt;!-- Filter Chain --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"&gt;&nbsp;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="filterInvocationDefinitionSource"&gt;&nbsp;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;&nbsp;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PATTERN_TYPE_APACHE_ANT&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
/**=</span><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">httpSessionContextIntegrationFilter,</span><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">authenticationProcessingFilter,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;exceptionTranslationFilter,filterSecurityInterceptor</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/value&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</span><br />
<br />
以下介绍主要的几个Filter：<br />
<ul>
    <li>Session Integration Filter</li>
</ul>
<div style="margin-left: 40px;">通常是Filter Chain中第一个Filter，会建立Security
Context 物件用以储存安全相关资讯，后续的Filter若有需要储存或取得安全相关资讯，即可利用Security Context
物件，如果Security Context物件中的资讯有所变动，Session Integration
Filter会将变动储存至Session物件之中，否则将Security
Context物件弃置，例如&#8220;第一个Acegi程式&#8221;中所使用的Authentication Processing
Filter，即利用Security Context物件来储存使用者名称、密码等使用者资讯。</div>
<br />
<ul>
    <li>Authentication Processing Filter</li>
</ul>
<div style="margin-left: 40px;">当使用者存取受保护资源而需要登入时，Authentication
Processing Filter可提供表单来源给使用者，之后从使用者的请求（物件）中取得名称、密码并建立authentication
token以储存资讯，接着将之交给验证管理员（authentication
manager）进行以进行使用者的比对，所以基本上，Authentication Processing Filter需要设定以下的资讯：</div>
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&lt;!-- 验证处理，使用表单 --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;bean id="authenticationProcessingFilter" </span><br />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 验证管理员，处理验证资讯提供者&nbsp; --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager"/&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 验证失败URL --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationFailureUrl" value="/acegilogin.jsp"/&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 验证成功预设URL --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultTargetUrl" value="/protected/userinfo.jsp"/&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 验证处理的提交位址 --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="filterProcessesUrl" value="<span style="color: #000000;">/j_acegi_security_check</span>"/&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</span><br />
</div>
<br />
<div style="margin-left: 40px;">当验证管理员进行使用者的比对、取得细节资讯并返回Authentication
Processing Filter后，Authentication Processing
Filter会建立Authentication并将取得的使用者资讯储存在Security
Context物件中，然后交给下一个Filter继续进行处理。</div>
<br />
<ul>
    <li>Exception Translation Filter</li>
</ul>
<div style="margin-left: 40px;">当验证或授权过程中发生例外时，Exception Translation Filter处理例外。<br />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;!-- 发生验证错误或权限错误时的处理 --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationEntryPoint"&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;bean
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;&nbsp;
</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;property name="loginFormUrl" value="/acegilogin.jsp"/&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;property name="forceHttps" value="false"/&gt;&nbsp;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="accessDeniedHandler"&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl"&gt;&nbsp;
</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;property name="errorPage" value="/accessDenied.jsp"/&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;/bean&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></div>
<br />
<ul>
    <li>Logout Processing Filter</li>
</ul>
<div style="margin-left: 40px;">管理登出的处理，实际的登出处理会交给Handler，您可以设置登出后的显示页面来源，在 <a href="http://caterpillar.onlyfun.net/GossipCN/AcegiGossip/FirstAcegLogoutCookies.html">第一个Acegi 程式 -&nbsp;登出、自动Cookies登入</a> 中，使用了SecurityContextLogoutHandler来让Session交效。</div>
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 登出处理 --&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;constructor-arg value="/acegilogin.jsp"/&gt; &lt;!-- 登出后的显示页面 --&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;constructor-arg&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;list&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;bean
class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/&gt;&nbsp;
</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/list&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/constructor-arg&gt;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;/bean&gt; </span><br />
</div>
<br />
<ul>
    <li>Interceptor filters</li>
</ul>
<div style="margin-left: 40px;">用来决定是否授权，验证与授权是一体的，所以在Authentication
Processing Filter之后必须设定Interceptor filters，Interceptor
filters使用您所设定的存取控制策略（access control
policy）来决定是否授权，一个使用者的存取控制策略定义了使用者、密码、角色等资讯：<br />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">caterpillar=123456,ROLE_SUPERVISOR</span></div>
<br />
<div style="margin-left: 40px;">您使用Interceptor filters来进行存取控制策略的设定，设定验证管理者与存取决策理员（Access Decision Manager），受保护的资源可存取之角色，存取决策管理员会以投票方式决定资源是否授权，例如：<br />
</div>
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;!-- FilterSecurityInterceptor 对 URI 进行保护 --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;bean id="filterSecurityInterceptor"</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 验证管理员 --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager" /&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 授权管理员 --&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="accessDecisionManager" ref="accessDecisionManager" /&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="objectDefinitionSource"&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PATTERN_TYPE_APACHE_ANT</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
/protected/**=ROLE_SUPERVISOR,ROLE_USER</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/value&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</span>
<img src ="http://www.blogjava.net/liuzheng/aggbug/231330.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-09-26 17:01 <a href="http://www.blogjava.net/liuzheng/articles/231330.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi基本配置 -信息放在数据库中</title><link>http://www.blogjava.net/liuzheng/articles/231327.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 26 Sep 2008 09:00:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/231327.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/231327.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/231327.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/231327.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/231327.html</trackback:ping><description><![CDATA[在先前的设定中，inMemoryDaoImpl将使用者讯息设定在userMap之中：<br />
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp; &lt;bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="userMap"&gt;&nbsp;&nbsp;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;&nbsp;&nbsp;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; caterpillar=123456,ROLE_SUPERVISOR</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user1=user1pwd,ROLE_USER</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
user2=user2pwd,disabled,ROLE_USER&nbsp;&nbsp;&nbsp;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/value&gt;&nbsp;&nbsp;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp; &lt;/bean&gt; </span><br />
</div>
<br />
您可以撰写一个属性档案/WEB-INF/users.properties：<br />
<ul>
    <li>users.properties</li>
</ul>
<pre>caterpillar=123456,ROLE_SUPERVISOR<br />
user1=user1pwd,ROLE_USER<br />
user2=user2pwd,disabled,ROLE_USER</pre>
<br />
然后改设定inMemoryDaoImpl的userProperties：<br />
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp; &lt;bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="userProperties"&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;bean
class="org.springframework.beans.factory.config.PropertiesFactoryBean"&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;property name="location" value="/WEB-INF/users.properties" /&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;" />
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">&nbsp;&nbsp; &lt;/bean&gt;</span><br />
</div>
<br />
如此在需要使用者讯息时，就可以从users.properties中提取。<br />
<br />
如果想要将使用者的相关讯息储存在资料库中，例如使用以下的SQL在MySQL中建立使用者讯息：<br />
<ul>
    <li>users.sql</li>
</ul>
<pre>CREATE DATABASE acegi;<br />
USE acegi;<br />
<br />
CREATE TABLE USERS(  <br />
USERNAME VARCHAR(50) NOT NULL PRIMARY KEY,  <br />
PASSWORD VARCHAR(50) NOT NULL,  <br />
ENABLED BIT NOT NULL<br />
);<br />
<br />
INSERT INTO USERS(username,password,enabled) values('caterpillar' ,'123456', 1);<br />
INSERT INTO USERS(username,password,enabled) values('user1' ,'user1pwd', 1);<br />
INSERT INTO USERS(username,password,enabled) values('user2' ,'user2pwd', 0);<br />
<br />
CREATE TABLE AUTHORITIES(  <br />
USERNAME VARCHAR( 50 ) NOT NULL,  <br />
AUTHORITY VARCHAR( 50 ) NOT NULL,  <br />
CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME)  <br />
);  <br />
<br />
INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( 'caterpillar' , 'ROLE_SUPERVISOR');<br />
INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( 'user1', 'ROLE_USER');  <br />
INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( 'user2', 'ROLE_USER');</pre>
<br />
您可以使用org.acegisecurity.userdetails.jdbc.JdbcDaoImpl作为userDetailsService，它需要一个DataSource，这可以使用Spring的DriverManagerDataSource，例如：<br />
<ul>
    <li>acegi-config.xml</li>
</ul>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"&gt;<br />
&lt;beans&gt;<br />
<span style="font-weight: bold;">    &lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">         &lt;property name="driverClassName"&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">             &lt;value&gt;com.mysql.jdbc.Driver&lt;/value&gt;              </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">         &lt;/property&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">         &lt;property name="url"&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">             &lt;value&gt;jdbc:mysql://localhost:3306/acegi&lt;/value&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">         &lt;/property&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">         &lt;property name="username"&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">             &lt;value&gt;root&lt;/value&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">         &lt;/property&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">         &lt;property name="password"&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">             &lt;value&gt;123456&lt;/value&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">         &lt;/property&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">     &lt;/bean&gt;  </span><br />
<br />
&lt;!-- 验证处理，使用表单 --&gt;<br />
&lt;bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;  <br />
&lt;!-- 验证管理员，处理验证资讯提供者  --&gt;<br />
&lt;property name="authenticationManager" ref="authenticationManager"/&gt;  <br />
&lt;!-- 验证失败URL --&gt;<br />
&lt;property name="authenticationFailureUrl" value="/acegilogin.jsp"/&gt;  <br />
&lt;!-- 验证成功预设URL --&gt;<br />
&lt;property name="defaultTargetUrl" value="/protected/loginsuccess.jsp"/&gt;  <br />
&lt;!-- 验证处理的提交位址 --&gt;<br />
&lt;property name="filterProcessesUrl" value="/j_acegi_security_check"/&gt;  <br />
&lt;/bean&gt;<br />
<br />
&lt;!-- 验证管理员，管理验证资讯提供者 --&gt;<br />
&lt;bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"&gt;  <br />
&lt;property name="providers"&gt;&lt;!-- 可有多个提供者,其中一个验证通过即可以了 --&gt;  <br />
&lt;list&gt;  <br />
&lt;ref local="daoAuthenticationProvider"/&gt; <br />
&lt;ref local="rememberMeAuthenticationProvider"/&gt;<br />
&lt;/list&gt;  <br />
&lt;/property&gt;  <br />
&lt;/bean&gt;<br />
<br />
&lt;!-- 验证提供者，指定使用资料库来源中的验证资讯 --&gt;<br />
&lt;bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;  <br />
&lt;property name="userDetailsService" ref="<span style="font-weight: bold;">jdbcDaoImpl</span>"/&gt;<br />
&lt;/bean&gt; <br />
<br />
<span style="font-weight: bold;">   &lt;bean id="jdbcDaoImpl" class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl"&gt;  </span><br style="font-weight: bold;" />
<span style="font-weight: bold;">       &lt;property name="dataSource" ref="dataSource"/&gt;</span><br style="font-weight: bold;" />
<span style="font-weight: bold;">   &lt;/bean&gt;  </span><br />
<br />
&lt;!-- 发生验证错误或权限错误时的处理 --&gt;<br />
&lt;bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"&gt;  <br />
&lt;property name="authenticationEntryPoint"&gt;  <br />
&lt;bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;  <br />
&lt;property name="loginFormUrl" value="/acegilogin.jsp"/&gt;  <br />
&lt;property name="forceHttps" value="false"/&gt;  <br />
&lt;/bean&gt;  <br />
&lt;/property&gt;  <br />
&lt;property name="accessDeniedHandler"&gt;  <br />
&lt;bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl"&gt;  <br />
&lt;property name="errorPage" value="/accessDenied.jsp"/&gt;  <br />
&lt;/bean&gt;  <br />
&lt;/property&gt;  <br />
&lt;/bean&gt;    <br />
<br />
&lt;!-- FilterSecurityInterceptor 对 URI 进行保护 --&gt;<br />
&lt;bean id="filterSecurityInterceptor"<br />
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br />
&lt;!-- 验证管理员 --&gt;<br />
&lt;property name="authenticationManager" ref="authenticationManager" /&gt;<br />
&lt;!-- 授权管理员 --&gt;<br />
&lt;property name="accessDecisionManager" ref="accessDecisionManager" /&gt;<br />
&lt;property name="objectDefinitionSource"&gt;<br />
&lt;value&gt;<br />
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br />
PATTERN_TYPE_APACHE_ANT<br />
/protected/**=ROLE_SUPERVISOR,ROLE_USER<br />
&lt;/value&gt;<br />
&lt;/property&gt;<br />
&lt;/bean&gt;<br />
<br />
&lt;!-- 授权管理员 --&gt;<br />
&lt;bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"&gt;<br />
&lt;!-- 是否全部弃权时视为通过 --&gt;<br />
&lt;property name="allowIfAllAbstainDecisions" value="false" /&gt;<br />
&lt;property name="decisionVoters"&gt;<br />
&lt;list&gt;<br />
&lt;bean class="org.acegisecurity.vote.RoleVoter" /&gt;<br />
&lt;/list&gt;<br />
&lt;/property&gt;<br />
&lt;/bean&gt;        <br />
<br />
&lt;!-- 利用cookie自动登入 --&gt;  <br />
&lt;bean id="rememberMeProcessingFilter"  <br />
class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter"&gt;  <br />
&lt;property name="authenticationManager"  ref="authenticationManager"/&gt;  <br />
&lt;property name="rememberMeServices" ref="rememberMeServices"/&gt;  <br />
&lt;/bean&gt;      <br />
&lt;bean id="rememberMeServices"  <br />
class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices"&gt;  <br />
&lt;property name="userDetailsService" ref="<span style="font-weight: bold;">jdbcDaoImpl</span>"/&gt;  <br />
&lt;property name="key" value="javauser"/&gt;  <br />
&lt;/bean&gt;<br />
&lt;bean id="rememberMeAuthenticationProvider"  <br />
class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"&gt;  <br />
&lt;property name="key" value="javauser"/&gt;  <br />
&lt;/bean&gt;   <br />
<br />
&lt;!-- 登出处理 --&gt;  <br />
&lt;bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"&gt;  <br />
&lt;constructor-arg value="/acegilogin.jsp"/&gt; &lt;!-- 登出后的显示页面 --&gt;  <br />
&lt;constructor-arg&gt;  <br />
&lt;list&gt;  <br />
&lt;bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/&gt;  <br />
&lt;/list&gt;  <br />
&lt;/constructor-arg&gt;  <br />
&lt;/bean&gt; <br />
<br />
&lt;bean id="httpSessionContextIntegrationFilter"<br />
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" /&gt;<br />
<br />
&lt;!-- Filter Chain --&gt;<br />
&lt;bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"&gt;  <br />
&lt;property name="filterInvocationDefinitionSource"&gt;  <br />
&lt;value&gt;  <br />
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON <br />
PATTERN_TYPE_APACHE_ANT <br />
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,<br />
filterSecurityInterceptor,logoutFilter,rememberMeProcessingFilter<br />
&lt;/value&gt; <br />
&lt;/property&gt; <br />
&lt;/bean&gt;    <br />
&lt;/beans&gt;   </pre>
<br />
当然，别忘了在您的Web应用程式的lib中，加入JDBC驱动程式程式库。
<img src ="http://www.blogjava.net/liuzheng/aggbug/231327.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-09-26 17:00 <a href="http://www.blogjava.net/liuzheng/articles/231327.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi学习笔记</title><link>http://www.blogjava.net/liuzheng/articles/231325.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 26 Sep 2008 08:59:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/231325.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/231325.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/231325.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/231325.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/231325.html</trackback:ping><description><![CDATA[很好的<br />
http://caterpillar.onlyfun.net/GossipCN/AcegiGossip/AcegiGossip.html<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/231325.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-09-26 16:59 <a href="http://www.blogjava.net/liuzheng/articles/231325.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个很好的学习CSS的网站</title><link>http://www.blogjava.net/liuzheng/articles/218288.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 29 Jul 2008 02:53:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/218288.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/218288.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/218288.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/218288.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/218288.html</trackback:ping><description><![CDATA[http://zh.html.net/tutorials/css/<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/218288.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-07-29 10:53 <a href="http://www.blogjava.net/liuzheng/articles/218288.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个很好的Unit测试例子，要满足xUnit开发</title><link>http://www.blogjava.net/liuzheng/articles/216547.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 22 Jul 2008 02:50:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/216547.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/216547.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/216547.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/216547.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/216547.html</trackback:ping><description><![CDATA[package nl.enovation.commons.monitor;<br />
<br />
import java.io.IOException;<br />
import java.util.ArrayList;<br />
import java.util.Arrays;<br />
import java.util.Collection;<br />
<br />
import javax.mail.MessagingException;<br />
import javax.mail.internet.MimeMessage;<br />
<br />
import nl.enovation.commons.IdGenerator;<br />
import nl.enovation.ems.domain.MailSender;<br />
<br />
import org.apache.commons.lang.math.RandomUtils;<br />
import org.hamcrest.Description;<br />
import org.hamcrest.Factory;<br />
import org.hamcrest.Matcher;<br />
import org.hamcrest.TypeSafeMatcher;<br />
import org.jmock.Expectations;<br />
import org.jmock.integration.junit4.JUnit4Mockery;<br />
import org.jmock.lib.legacy.ClassImposteriser;<br />
import org.junit.Assert;<br />
import org.junit.Before;<br />
import org.junit.Ignore;<br />
import org.junit.Test;<br />
<br />
import static nl.enovation.commons.monitor.HasProperty.property;<br />
import static nl.enovation.commons.monitor.HasStringCollectionValues.stringCollectionValues;<br />
import static nl.enovation.commons.monitor.HasContent.contentValue;<br />
import static org.junit.Assert.fail;<br />
<br />
public class MailLoopMonitor_check_Test {<br />
<br />
&nbsp;&nbsp;&nbsp; private MailLoopMonitor mailLoopMonitor;<br />
<br />
&nbsp;&nbsp;&nbsp; protected JUnit4Mockery context = new JUnit4Mockery() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setImposteriser(ClassImposteriser.INSTANCE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; };<br />
<br />
&nbsp;&nbsp;&nbsp; private MailSender mailSender;<br />
<br />
&nbsp;&nbsp;&nbsp; private String generatedId;<br />
<br />
&nbsp;&nbsp;&nbsp; private MailChecker mailChecker;<br />
<br />
&nbsp;&nbsp;&nbsp; @Before<br />
&nbsp;&nbsp;&nbsp; public void setUp() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor = new MailLoopMonitor();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // mailChecker = context.mock(MailChecker.class);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithTheConfiguredSender() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final String sender = createRandomEmailAddress();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(sender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(any(MimeMessage.class)), with(equal(sender)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithTheConfiguredRecipient() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final String recipient = createRandomEmailAddress();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final Collection&lt;String&gt; recipients = new ArrayList&lt;String&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; recipients.add(recipient);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(recipient);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(any(MimeMessage.class)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(stringCollectionValues(recipients)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithTheConfiguredContent() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final String messageContent = createRandomString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(messageContent);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(contentValue(messageContent)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; @Ignore("specification not implemented yet")<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithUniqueIds() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fail("not implemented yet");<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldObtainTheUniqueIdsFromTheConfiguredIdGenerator() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(property("Subject", generatedId)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithTheConfiguredMailSender() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exactly(2).of(mailSender).sendMessage(with(any(MimeMessage.class)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldCheckMailWithTheConfiguredMailChecker() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailChecker = context.mock(MailChecker.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(createMailSender());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(mailChecker);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exactly(2).of(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldReturnStatusCriticalIfSendingMailFails() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.message should contain "failed to send message"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.name should equal the name parameter passed to the check()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // method<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.cause should equal the exception that caused the failure<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final Exception exception = new MessagingException("sending message failure.");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(any(MimeMessage.class)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(throwException(exception));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status status = mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals(Status.Code.CRITICAL, status.getCode());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals("failed to send message", status.getMessage());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals(exception, status.getCause());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; @Ignore("specification not implemented yet")<br />
&nbsp;&nbsp;&nbsp; public void shouldWaitInitialWaitTimeAfterSendingMailBeforeCheckingMail() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fail("not implemented yet");<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldReturnStatusOKIfCheckingMailSucceeds() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.message should contain "matched message ID " + the unique id<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // of the sent Mail<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.name should equal the name parameter passed to the check()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // method<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.cause should be null<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(createMailSender());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status status = mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals(Status.Code.OK, status.getCode());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals("matched message ID " + generatedId, status.getMessage());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals("test", status.getServiceName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertNull(status.getCause());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; @Ignore("specification not implemented yet")<br />
&nbsp;&nbsp;&nbsp; public void shouldRetryAfterConfiguredIntervalTimeIfCheckingMailFailsAndNotYetDoneMaximumTries() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fail("not implemented yet");<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldReturnStatusCriticalIfCheckingMailFailsAndDoneMaximumTries() throws MessagingException,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.message should contain "message ID " + the unique id of the<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // sent Mail + " not matched"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.name should equal the name parameter passed to the check()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // method<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.cause should equal the exception that caused the failure<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final int checkTimes = 3;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(createMailSender());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailCheckerWithExactCheckTimes(false,checkTimes));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(checkTimes);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status status = mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals(Status.Code.CRITICAL, status.getCode());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals("message ID " + generatedId + " not matched", status.getMessage());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertNull(status.getCause());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; /* new specs, add only after the specs above have been implemented */<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithUniqueIdInSubjectHeaderIfSoConfigured() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(property("Subject", generatedId)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; @Ignore("specification not implemented yet")<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithUniqueIdInMessageIdHeaderIfSoConfigured() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(property("Message-ID", "&lt;"+generatedId+"&gt;")), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fail("not implemented yet");<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private String createRandomEmailAddress() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "user" + RandomUtils.nextInt() + "@domain" + RandomUtils.nextInt() + ".example.com";<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private IdGenerator&lt;String&gt; createAnIdGenerator() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final IdGenerator&lt;String&gt; idGenerator = context.mock(IdGenerator.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; generatedId = createRandomString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allowing(idGenerator).getNextId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(generatedId));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return idGenerator;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private String createRandomString() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return String.valueOf(RandomUtils.nextInt());<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private MailChecker createMailChecker(boolean result) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final MailChecker mailChecker;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailChecker = context.mock(MailChecker.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (result) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allowing(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allowing(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(false));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return mailChecker;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private MailChecker createMailCheckerWithExactCheckTimes(boolean result, final int i) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final MailChecker mailChecker;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailChecker = context.mock(MailChecker.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (result) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exactly(i).of(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exactly(i).of(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(false));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return mailChecker;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private MailSender createMailSender() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final MailSender mailSender;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allowing(mailSender).sendMessage(with(any(MimeMessage.class)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return mailSender;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
}<br />
<br />
// TODO:<br />
<br />
class HasProperty extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String name;<br />
<br />
&nbsp;&nbsp;&nbsp; private String value;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasProperty(String name, String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.name = name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.value = value;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] headers = item.getHeader(name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (value == null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (headers == null || headers.length == 0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (headers == null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (String header : headers) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (header.equals(value))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; property(String name, String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasProperty(name, value);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasContent extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String value;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasContent(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.value = value;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String content = (String) item.getContent();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (value == null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (value.equals(content)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (IOException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; contentValue(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasContent(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasStringValue extends TypeSafeMatcher&lt;String&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String value;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasStringValue(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.value = value;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.value.equals(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;String&gt; stringValue(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasStringValue(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasStringCollectionValues extends TypeSafeMatcher&lt;Collection&lt;String&gt;&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private Collection&lt;String&gt; values;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasStringCollectionValues(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; values = new ArrayList&lt;String&gt;(Arrays.asList(value.split(",")));<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public HasStringCollectionValues(Collection&lt;String&gt; values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.values = values;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(Collection&lt;String&gt; values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.values.containsAll(values);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;Collection&lt;String&gt;&gt; stringCollectionValues(String values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasStringCollectionValues(values);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;Collection&lt;String&gt;&gt; stringCollectionValues(Collection&lt;String&gt; values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasStringCollectionValues(values);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasIntValue extends TypeSafeMatcher&lt;Integer&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private Integer value;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasIntValue(Integer value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.value = value;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(Integer value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.value.equals(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;Integer&gt; intValue(Integer value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasIntValue(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasSubject extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String subject;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasSubject(String subject) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.subject = subject;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return item.getSubject().equals(subject);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; subject(String subject) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasSubject(subject);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasMessageId extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String messageId;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasMessageId(String messageId) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.messageId = messageId;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return item.getMessageID().equals(messageId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; messageId(String messageId) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasMessageId(messageId);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasFileName extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String fileName;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasFileName(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.fileName = fileName;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return item.getFileName().equals(fileName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; fileName(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasFileName(fileName);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/216547.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-07-22 10:50 <a href="http://www.blogjava.net/liuzheng/articles/216547.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Jmock测试Webservice的应用</title><link>http://www.blogjava.net/liuzheng/articles/216545.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 22 Jul 2008 02:48:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/216545.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/216545.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/216545.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/216545.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/216545.html</trackback:ping><description><![CDATA[package nl.enovation.commons.monitor;<br />
<br />
import static org.junit.Assert.assertFalse;<br />
import static org.junit.Assert.assertTrue;<br />
<br />
import javax.xml.bind.JAXBElement;<br />
<br />
import nl.enovation.ems.archivewebservice.schema.Message;<br />
import nl.enovation.ems.archivewebservice.schema.MessageInternalIdRquest;<br />
import nl.enovation.ems.archivewebservice.schema.ObjectFactory;<br />
<br />
import org.apache.commons.lang.math.RandomUtils;<br />
import org.jmock.Expectations;<br />
import org.jmock.Mockery;<br />
import org.jmock.integration.junit4.JUnit4Mockery;<br />
import org.jmock.lib.legacy.ClassImposteriser;<br />
import org.junit.Before;<br />
import org.junit.Test;<br />
import org.springframework.ws.client.WebServiceFaultException;<br />
import org.springframework.ws.client.core.WebServiceTemplate;<br />
<br />
<br />
public class MailArchiveWebserviceCheckerTest {<br />
<br />
&nbsp;&nbsp;&nbsp; private MailArchiveWebserviceChecker mailArchiveWebserviceChecker = new MailArchiveWebserviceChecker();<br />
<br />
&nbsp;&nbsp;&nbsp; private WebServiceTemplate webServiceTemplate;<br />
<br />
&nbsp;&nbsp;&nbsp; private ObjectFactory objectFactory;<br />
<br />
&nbsp;&nbsp;&nbsp; private Mockery context = new JUnit4Mockery() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setImposteriser(ClassImposteriser.INSTANCE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; };<br />
<br />
&nbsp;&nbsp;&nbsp; private MessageInternalIdRquest messageInternalIdRquest;<br />
<br />
&nbsp;&nbsp;&nbsp; private Message message;<br />
<br />
&nbsp;&nbsp;&nbsp; @Before<br />
&nbsp;&nbsp;&nbsp; public void setUp() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; webServiceTemplate = context.mock(WebServiceTemplate.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; objectFactory = new ObjectFactory();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; messageInternalIdRquest = objectFactory.createMessageInternalIdRquest();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; messageInternalIdRquest.setMessageInternalId(createRandomMessageId());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; message = objectFactory.createMessage();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailArchiveWebserviceChecker.setWebServiceTemplate(webServiceTemplate);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldReturnTrue() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String messageId = createRandomMessageId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; message.setMessageId(messageId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createMockedResponse();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertTrue(mailArchiveWebserviceChecker.check(messageId));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldReturnFalse() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String messageId = createRandomMessageId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String anotherMessageId = createRandomMessageId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; message.setMessageId(messageId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createMockedResponse();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertFalse(mailArchiveWebserviceChecker.check(anotherMessageId));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldReturnFalseCausedByException() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String messageId = createRandomMessageId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String anotherMessageId = createRandomMessageId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; message.setMessageId(messageId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createMockedExceptionResponse();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertFalse(mailArchiveWebserviceChecker.check(anotherMessageId));<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private String createRandomMessageId() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return String.valueOf(RandomUtils.nextInt());<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private void createMockedResponse() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final JAXBElement&lt;Message&gt; retrieveResponseObj = objectFactory<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .createRetrieveMessageThroughMessageIdResponse(message);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // expectations<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(webServiceTemplate).marshalSendAndReceive(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(objectFactory.createMessageInternalIdRquest().getClass())));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(retrieveResponseObj));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private void createMockedExceptionResponse() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // expectations<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(webServiceTemplate).marshalSendAndReceive(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(objectFactory.createMessageInternalIdRquest().getClass())));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(throwException(new WebServiceFaultException("")));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/216545.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-07-22 10:48 <a href="http://www.blogjava.net/liuzheng/articles/216545.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>本人常用的快捷键的修改</title><link>http://www.blogjava.net/liuzheng/articles/216543.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 22 Jul 2008 02:47:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/216543.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/216543.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/216543.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/216543.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/216543.html</trackback:ping><description><![CDATA[Eclipse中提供了很多快捷键的修改，本人一般修改Word Completion 和Content Assit 改为Alt+.<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/216543.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-07-22 10:47 <a href="http://www.blogjava.net/liuzheng/articles/216543.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个很好的hamcrest的例子</title><link>http://www.blogjava.net/liuzheng/articles/215695.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 18 Jul 2008 02:52:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/215695.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/215695.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/215695.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/215695.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/215695.html</trackback:ping><description><![CDATA[package nl.enovation.commons.monitor;<br />
<br />
import java.io.IOException;<br />
import java.util.ArrayList;<br />
import java.util.Arrays;<br />
import java.util.Collection;<br />
<br />
import javax.mail.MessagingException;<br />
import javax.mail.internet.MimeMessage;<br />
<br />
import nl.enovation.commons.IdGenerator;<br />
import nl.enovation.ems.domain.MailSender;<br />
<br />
import org.apache.commons.lang.math.RandomUtils;<br />
import org.hamcrest.Description;<br />
import org.hamcrest.Factory;<br />
import org.hamcrest.Matcher;<br />
import org.hamcrest.TypeSafeMatcher;<br />
import org.jmock.Expectations;<br />
import org.jmock.integration.junit4.JUnit4Mockery;<br />
import org.jmock.lib.legacy.ClassImposteriser;<br />
import org.junit.Assert;<br />
import org.junit.Before;<br />
import org.junit.Ignore;<br />
import org.junit.Test;<br />
<br />
import static nl.enovation.commons.monitor.HasProperty.property;<br />
import static nl.enovation.commons.monitor.HasStringCollectionValues.stringCollectionValues;<br />
import static nl.enovation.commons.monitor.HasContent.contentValue;<br />
import static org.junit.Assert.fail;<br />
<br />
public class MailLoopMonitor_check_Test {<br />
<br />
&nbsp;&nbsp;&nbsp; private MailLoopMonitor mailLoopMonitor;<br />
<br />
&nbsp;&nbsp;&nbsp; protected JUnit4Mockery context = new JUnit4Mockery() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setImposteriser(ClassImposteriser.INSTANCE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; };<br />
<br />
&nbsp;&nbsp;&nbsp; private MailSender mailSender;<br />
<br />
&nbsp;&nbsp;&nbsp; private String generatedId;<br />
<br />
&nbsp;&nbsp;&nbsp; private MailChecker mailChecker;<br />
<br />
&nbsp;&nbsp;&nbsp; @Before<br />
&nbsp;&nbsp;&nbsp; public void setUp() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor = new MailLoopMonitor();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // mailChecker = context.mock(MailChecker.class);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithTheConfiguredSender() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final String sender = createRandomEmailAddress();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(sender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(any(MimeMessage.class)), with(equal(sender)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithTheConfiguredRecipient() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final String recipient = createRandomEmailAddress();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final Collection&lt;String&gt; recipients = new ArrayList&lt;String&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; recipients.add(recipient);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(recipient);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(any(MimeMessage.class)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(stringCollectionValues(recipients)));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithTheConfiguredContent() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final String messageContent = createRandomString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(messageContent);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(contentValue(messageContent)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; @Ignore("specification not implemented yet")<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithUniqueIds() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fail("not implemented yet");<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldObtainTheUniqueIdsFromTheConfiguredIdGenerator() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(property("Subject", generatedId)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithTheConfiguredMailSender() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exactly(2).of(mailSender).sendMessage(with(any(MimeMessage.class)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldCheckMailWithTheConfiguredMailChecker() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailChecker = context.mock(MailChecker.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(createMailSender());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(mailChecker);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exactly(2).of(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldReturnStatusCriticalIfSendingMailFails() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.message should contain "failed to send message"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.name should equal the name parameter passed to the check()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // method<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.cause should equal the exception that caused the failure<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final Exception exception = new MessagingException("sending message failure.");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(any(MimeMessage.class)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(throwException(exception));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status status = mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals(Status.Code.CRITICAL, status.getCode());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals("failed to send message", status.getMessage());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals(exception, status.getCause());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; @Ignore("specification not implemented yet")<br />
&nbsp;&nbsp;&nbsp; public void shouldWaitInitialWaitTimeAfterSendingMailBeforeCheckingMail() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fail("not implemented yet");<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldReturnStatusOKIfCheckingMailSucceeds() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.message should contain "matched message ID " + the unique id<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // of the sent Mail<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.name should equal the name parameter passed to the check()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // method<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.cause should be null<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(createMailSender());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status status = mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals(Status.Code.OK, status.getCode());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals("matched message ID " + generatedId, status.getMessage());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals("test", status.getServiceName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertNull(status.getCause());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; @Ignore("specification not implemented yet")<br />
&nbsp;&nbsp;&nbsp; public void shouldRetryAfterConfiguredIntervalTimeIfCheckingMailFailsAndNotYetDoneMaximumTries() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fail("not implemented yet");<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldReturnStatusCriticalIfCheckingMailFailsAndDoneMaximumTries() throws MessagingException,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.message should contain "message ID " + the unique id of the<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // sent Mail + " not matched"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.name should equal the name parameter passed to the check()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // method<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // status.cause should equal the exception that caused the failure<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final int checkTimes = 3;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(createMailSender());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailCheckerWithExactCheckTimes(false,checkTimes));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(checkTimes);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status status = mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals(Status.Code.CRITICAL, status.getCode());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertEquals("message ID " + generatedId + " not matched", status.getMessage());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert.assertNull(status.getCause());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; /* new specs, add only after the specs above have been implemented */<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithUniqueIdInSubjectHeaderIfSoConfigured() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(property("Subject", generatedId)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; @Ignore("specification not implemented yet")<br />
&nbsp;&nbsp;&nbsp; public void shouldSendMailWithUniqueIdInMessageIdHeaderIfSoConfigured() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setSender(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIdGenerator(createAnIdGenerator());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailSender(mailSender);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMailChecker(createMailChecker(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setMessage(createRandomString());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setInitialWaitTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setIntervalTime(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setCheckTimes(1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.setRecipient(createRandomEmailAddress());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; one(mailSender).sendMessage(with(property("Message-ID", "&lt;"+generatedId+"&gt;")), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailLoopMonitor.check("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // verify<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.assertIsSatisfied();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fail("not implemented yet");<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private String createRandomEmailAddress() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "user" + RandomUtils.nextInt() + "@domain" + RandomUtils.nextInt() + ".example.com";<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private IdGenerator&lt;String&gt; createAnIdGenerator() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final IdGenerator&lt;String&gt; idGenerator = context.mock(IdGenerator.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; generatedId = createRandomString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allowing(idGenerator).getNextId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(generatedId));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return idGenerator;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private String createRandomString() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return String.valueOf(RandomUtils.nextInt());<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private MailChecker createMailChecker(boolean result) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final MailChecker mailChecker;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailChecker = context.mock(MailChecker.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (result) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allowing(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allowing(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(false));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return mailChecker;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private MailChecker createMailCheckerWithExactCheckTimes(boolean result, final int i) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final MailChecker mailChecker;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailChecker = context.mock(MailChecker.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (result) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exactly(i).of(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(true));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exactly(i).of(mailChecker).check(with(any(String.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will(returnValue(false));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return mailChecker;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; private MailSender createMailSender() throws MessagingException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final MailSender mailSender;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mailSender = context.mock(MailSender.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.checking(new Expectations() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allowing(mailSender).sendMessage(with(any(MimeMessage.class)), with(any(String.class)),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with(any(Collection.class)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return mailSender;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
}<br />
<br />
// TODO:<br />
<br />
class HasProperty extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String name;<br />
<br />
&nbsp;&nbsp;&nbsp; private String value;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasProperty(String name, String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.name = name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.value = value;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] headers = item.getHeader(name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (value == null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (headers == null || headers.length == 0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (headers == null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (String header : headers) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (header.equals(value))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; property(String name, String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasProperty(name, value);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasContent extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String value;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasContent(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.value = value;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String content = (String) item.getContent();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (value == null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (value.equals(content)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (IOException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; contentValue(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasContent(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasStringValue extends TypeSafeMatcher&lt;String&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String value;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasStringValue(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.value = value;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.value.equals(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;String&gt; stringValue(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasStringValue(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasStringCollectionValues extends TypeSafeMatcher&lt;Collection&lt;String&gt;&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private Collection&lt;String&gt; values;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasStringCollectionValues(String value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; values = new ArrayList&lt;String&gt;(Arrays.asList(value.split(",")));<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public HasStringCollectionValues(Collection&lt;String&gt; values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.values = values;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(Collection&lt;String&gt; values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.values.containsAll(values);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;Collection&lt;String&gt;&gt; stringCollectionValues(String values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasStringCollectionValues(values);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;Collection&lt;String&gt;&gt; stringCollectionValues(Collection&lt;String&gt; values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasStringCollectionValues(values);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasIntValue extends TypeSafeMatcher&lt;Integer&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private Integer value;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasIntValue(Integer value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.value = value;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(Integer value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.value.equals(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;Integer&gt; intValue(Integer value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasIntValue(value);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasSubject extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String subject;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasSubject(String subject) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.subject = subject;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return item.getSubject().equals(subject);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; subject(String subject) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasSubject(subject);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasMessageId extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String messageId;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasMessageId(String messageId) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.messageId = messageId;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return item.getMessageID().equals(messageId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; messageId(String messageId) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasMessageId(messageId);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
class HasFileName extends TypeSafeMatcher&lt;MimeMessage&gt; {<br />
<br />
&nbsp;&nbsp;&nbsp; private String fileName;<br />
<br />
&nbsp;&nbsp;&nbsp; public HasFileName(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.fileName = fileName;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; public void describeTo(Description description) {<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Override<br />
&nbsp;&nbsp;&nbsp; public boolean matchesSafely(MimeMessage item) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return item.getFileName().equals(fileName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (MessagingException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Factory<br />
&nbsp;&nbsp;&nbsp; static &lt;T&gt; Matcher&lt;MimeMessage&gt; fileName(String fileName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new HasFileName(fileName);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/215695.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-07-18 10:52 <a href="http://www.blogjava.net/liuzheng/articles/215695.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个很好的DBunit test的架构，和Hibernate结合</title><link>http://www.blogjava.net/liuzheng/articles/215691.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 18 Jul 2008 02:49:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/215691.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/215691.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/215691.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/215691.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/215691.html</trackback:ping><description><![CDATA[最基本的创建数据库和打开关闭数据库使用父类<br />
public abstract class HibernateBaseTest {<br />
<br />
&nbsp;&nbsp;&nbsp; protected ApplicationContext applicationContext;<br />
<br />
&nbsp;&nbsp;&nbsp; protected HibernateTransactionManager hibernateTransactionManager;<br />
<br />
&nbsp;&nbsp;&nbsp; protected IDatabaseTester databaseTester;<br />
<br />
&nbsp;&nbsp;&nbsp; @Before<br />
&nbsp;&nbsp;&nbsp; public void setUpHibernate() throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // BasicConfigurator.configure(); /* for debugging */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; applicationContext = new ClassPathXmlApplicationContext("hibernate-hsql-context.xml");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hibernateTransactionManager = (HibernateTransactionManager) applicationContext<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .getBean("hibernateTransactionManager");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; databaseTester = (IDatabaseTester) applicationContext.getBean("dbUnitJdbcDatabaseTester");<br />
<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; protected QueryDataSet createQueryDataSet() throws SQLException, Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QueryDataSet qds = new QueryDataSet(databaseTester.getConnection());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("account");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return qds;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @After<br />
&nbsp;&nbsp;&nbsp; public void tearDown() throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (databaseTester != null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; databaseTester.setTearDownOperation(DatabaseOperation.DELETE_ALL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; databaseTester.onTearDown();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
配置文件为：<br />
&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />
&lt;beans xmlns="http://www.springframework.org/schema/beans"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:context="http://www.springframework.org/schema/context"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:util="http://www.springframework.org/schema/util"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xsi:schemaLocation="<br />
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd<br />
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd<br />
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"&gt;<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="targetClass" value="nl.enovation.ems.message.repository.DataSourceUserType"/&gt;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="targetMethod" value="setDefaultDataSourceRepository"/&gt;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="arguments"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;list&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;ref bean="dataSourceRepository"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/list&gt;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;bean id="dataSourceRepository" class="nl.enovation.ems.message.repository.FileSectionDataSourceRepository"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="baseDir" value="/var/lib/ems/messages"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;bean id="jdbcDataSourceRepository" class="nl.enovation.ems.message.repository.JdbcDataSourceRepository"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="jdbcTemplate" ref="jdbcTemplate"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="lobHandler" ref="lobHandler"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;ref local="hibernateSessionFactory" /&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="hibernateSessionFactory"<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="mappingResources"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;list&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;value&gt;META-INF/Message.hbm.xml&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;value&gt;META-INF/Account.hbm.xml&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;value&gt;META-INF/Rule.hbm.xml&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;value&gt;META-INF/Destination.hbm.xml&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;value&gt;META-INF/MessageSearchCriteria.hbm.xml&lt;/value&gt;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;value&gt;META-INF/ReportRule.hbm.xml&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;value&gt;META-INF/NotificationRule.hbm.xml&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;value&gt;META-INF/InboxEntry.hbm.xml&lt;/value&gt;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/list&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="hibernateProperties"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;props&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.dialect"&gt;org.hibernate.dialect.HSQLDialect&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.connection.driver_class"&gt;org.hsqldb.jdbcDriver&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.connection.url"&gt;jdbc:hsqldb:mem:ems&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.connection.username"&gt;sa&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.connection.password"&gt;&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.connection.pool_size"&gt;1&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.connection.autocommit"&gt;true&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.cache.provider_class"&gt;org.hibernate.cache.HashtableCacheProvider&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;prop key="org.hibernate.SQL"&gt;true&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.hbm2ddl.auto"&gt;create&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/props&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="hibernateTransactionManager"<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;ref bean="hibernateSessionFactory" /&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;bean name="dbUnitJdbcDatabaseTester" class="nl.enovation.ems.domain.BooleanSupportingJdbcDatabaseTester"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;constructor-arg value="org.hibernate.dialect.HSQLDialect"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;constructor-arg value="jdbc:hsqldb:mem:ems"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="username" value="sa"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="password" value=""/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;bean id="accountRepository" class="nl.enovation.ems.domain.HibernateAccountRepository"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="hibernateTemplate"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;ref bean="hibernateTemplate"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;bean id="messageRepository" class="nl.enovation.ems.domain.HibernateMessageRepository"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="hibernateTemplate"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;ref bean="hibernateTemplate"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="hibernateQueryManager"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;ref local="hibernateQueryManager" /&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;bean id="hibernateQueryManager" class="nl.enovation.commons.query.hibernate.HibernateQueryManager"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="idGenerator"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;bean class="nl.enovation.commons.SimpleIdGenerator"/&gt;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;ref local="hibernateSessionFactory"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="maxIdleTime" value="30000"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="maxResults" value="1501"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;bean id="jdbcMessageRepository"<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; class="nl.enovation.ems.domain.JdbcMessageRepository"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="jdbcTemplate"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;ref local="jdbcTemplate" /&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="contentPersister"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;bean class="nl.enovation.ems.message.repository.test.DataSourcePersisterMock"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="dataSource" ref="dataSource" /&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;bean id="jdbcTransactionManager"<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="dataSource" ref="dataSource" /&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="dataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="driverClassName" value="org.hsqldb.jdbcDriver"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="url" value="jdbc:hsqldb:mem:ems"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="username" value="sa"/&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="password" value=""/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="lobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="nativeJdbcExtractor"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;bean class="org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor" /&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
<br />
&nbsp;&nbsp;&nbsp; &lt;bean class="nl.enovation.ems.message.repository.DataSourceUserType" scope="prototype"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="dataSourceRepository"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;bean class="nl.enovation.ems.message.repository.JdbcDataSourceRepository"&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="jdbcTemplate" ref="jdbcTemplate" /&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;property name="lobHandler" ref="lobHandler" /&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;&nbsp;&nbsp;&nbsp; <br />
&lt;/beans&gt;<br />
<br />
<br />
一个测试类为：<br />
package nl.enovation.ems.domain;<br />
<br />
import java.sql.SQLException;<br />
import java.util.Set;<br />
<br />
import javax.activation.DataSource;<br />
<br />
import nl.enovation.commons.datasource.FileSectionDataSource;<br />
<br />
import org.dbunit.Assertion;<br />
import org.dbunit.database.QueryDataSet;<br />
import org.dbunit.dataset.CompositeDataSet;<br />
import org.dbunit.dataset.IDataSet;<br />
import org.dbunit.dataset.ITable;<br />
import org.dbunit.dataset.filter.DefaultColumnFilter;<br />
import org.dbunit.dataset.xml.XmlDataSet;<br />
import org.junit.Before;<br />
import org.junit.Ignore;<br />
import org.junit.Test;<br />
import org.springframework.core.io.Resource;<br />
import org.springframework.transaction.TransactionDefinition;<br />
import org.springframework.transaction.TransactionStatus;<br />
import org.springframework.transaction.support.DefaultTransactionDefinition;<br />
<br />
import static org.junit.Assert.assertEquals;<br />
import static org.junit.Assert.assertNotNull;<br />
import static org.junit.Assert.assertThat;<br />
import static org.hamcrest.CoreMatchers.*;<br />
<br />
public class HibernateInboxPersistenceTest extends HibernateBaseTest {<br />
<br />
&nbsp;&nbsp;&nbsp; private AccountRepository accountRepository;<br />
<br />
&nbsp;&nbsp;&nbsp; private MessageRepository messageRepository;<br />
<br />
&nbsp;&nbsp;&nbsp; private IDataSet addExpectedDataSet;<br />
<br />
&nbsp;&nbsp;&nbsp; private CompositeDataSet deleteExpectedDataSet;<br />
<br />
&nbsp;&nbsp;&nbsp; @Before<br />
&nbsp;&nbsp;&nbsp; public void setUp() throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; messageRepository = (MessageRepository) applicationContext.getBean("messageRepository");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; accountRepository = (AccountRepository) applicationContext.getBean("accountRepository");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Resource inboxDataSetResource = applicationContext.getResource("store-inbox-dataset.xml");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Resource messageDataSetResource = applicationContext.getResource("store-message-dataset.xml");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Resource accountDataSetResource = applicationContext.getResource("shared-account-dataset.xml");<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CompositeDataSet ds = new CompositeDataSet(new IDataSet[] {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new XmlDataSet(accountDataSetResource.getInputStream()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //new XmlDataSet(messageDataSetResource.getInputStream()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new XmlDataSet(inboxDataSetResource.getInputStream()) });<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; databaseTester.setDataSet(ds);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Resource inboxAddDataSetResource = applicationContext.getResource("store-inbox-add-assert-dataset.xml");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addExpectedDataSet = new CompositeDataSet(new IDataSet[] {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new XmlDataSet(accountDataSetResource.getInputStream()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //new XmlDataSet(messageDataSetResource.getInputStream()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new XmlDataSet(inboxAddDataSetResource.getInputStream()) });<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Resource inboxDeleteDataSetResource = applicationContext.getResource("store-inbox-delete-assert-dataset.xml");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; messageDataSetResource = applicationContext.getResource("store-message-inbox-delete-dataset.xml");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; deleteExpectedDataSet = new CompositeDataSet(new IDataSet[] {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new XmlDataSet(accountDataSetResource.getInputStream()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //new XmlDataSet(messageDataSetResource.getInputStream()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new XmlDataSet(inboxDeleteDataSetResource.getInputStream()) });<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; databaseTester.onSetup();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void testAddInboxEntry() throws SQLException, Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultTransactionDefinition txdef = new DefaultTransactionDefinition();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; txdef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TransactionStatus tx = hibernateTransactionManager.getTransaction(txdef);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Account account = accountRepository.findById("1");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(account);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Message message = messageRepository.findById("2@lms");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(message);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; account.addToInbox(message);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hibernateTransactionManager.commit(tx);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QueryDataSet qds = new QueryDataSet(databaseTester.getConnection());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("inbox");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("account");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("message");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("destination");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("contentstatistic");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assertion.assertEquals(addExpectedDataSet, qds);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void testRetrieveInboxEntry() throws SQLException, Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultTransactionDefinition txdef = new DefaultTransactionDefinition();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; txdef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TransactionStatus tx = hibernateTransactionManager.getTransaction(txdef);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Account account = accountRepository.findById("1");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(account);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Message message = messageRepository.findById("2@lms");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(message);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; account.addToInbox(message);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hibernateTransactionManager.commit(tx);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TransactionStatus tx = hibernateTransactionManager.getTransaction(txdef);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Account account = accountRepository.findById("1");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(account);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Set&lt;InboxEntry&gt; inbox = account.getInbox();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InboxEntry entry = inbox.iterator().next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertEquals("2@lms", entry.getMessage().getId());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataSource dataSource = entry.getContent();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertThat(dataSource, instanceOf(FileSectionDataSource.class));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertThat(dataSource.getName(), is("20070904.0:656071:1723"));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hibernateTransactionManager.commit(tx);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; @Ignore(value = "Should not throw an exception, but HSQLDB does")<br />
&nbsp;&nbsp;&nbsp; public void testAddDuplicateInboxEntry() throws SQLException, Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultTransactionDefinition txdef = new DefaultTransactionDefinition();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; txdef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TransactionStatus tx = hibernateTransactionManager.getTransaction(txdef);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Account account = accountRepository.findById("1");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(account);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Message message = messageRepository.findById("2@lms");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(message);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; account.addToInbox(message);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; account.addToInbox(message);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hibernateTransactionManager.commit(tx);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QueryDataSet qds = new QueryDataSet(databaseTester.getConnection());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("inbox");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("account");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("message");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("destination");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("contentstatistic");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assertion.assertEquals(addExpectedDataSet, qds);<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; @Test<br />
&nbsp;&nbsp;&nbsp; public void testDeleteInboxEntry() throws SQLException, Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DefaultTransactionDefinition txdef = new DefaultTransactionDefinition();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; txdef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TransactionStatus tx = hibernateTransactionManager.getTransaction(txdef);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Account account = accountRepository.findById("2");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(account);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Message message = messageRepository.findById("2@lms");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(message);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InboxEntry entry = account.getInbox().iterator().next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; entry.delete();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // account.removeFromInbox(new InboxEntry(account, message));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hibernateTransactionManager.commit(tx);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QueryDataSet qds = new QueryDataSet(databaseTester.getConnection());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("inbox");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("account");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("message");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("destination");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qds.addTable("contentstatistic");<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ITable filteredTable = DefaultColumnFilter.excludedColumnsTable(qds.getTable("destination"),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new String[] { "statetimestamp" });<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assertion.assertEquals(deleteExpectedDataSet.getTable("destination"), filteredTable);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Assertion.assertEquals(deleteExpectedDataSet,qds);<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/215691.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-07-18 10:49 <a href="http://www.blogjava.net/liuzheng/articles/215691.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The Hamcrest Tutorial ---一个很好的单元测试工具</title><link>http://www.blogjava.net/liuzheng/articles/198642.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 06 May 2008 02:01:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/198642.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/198642.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/198642.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/198642.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/198642.html</trackback:ping><description><![CDATA[<h1><a id="The_Hamcrest_Tutorial">The Hamcrest Tutorial</h1>
<h2><a id="Introduction">Introduction</h2>
<p>Hamcrest is a framework for writing matcher objects allowing 'match' rules to be defined declaratively. There are a number of situations where matchers are invaluble, such as UI validation, or data filtering, but it is in the area of writing flexible tests that matchers are most commonly used. This tutorial shows you how to use Hamcrest for unit testing. </p>
<p>When writing tests it is sometimes difficult to get the balance right between overspecifying the test (and making it brittle to changes), and not specifying enough (making the test less valuable since it continues to pass even when the thing being tested is broken). Having a tool that allows you to pick out precisely the aspect under test and describe the values it should have, to a controlled level of precision, helps greatly in writing tests that are "just right". Such tests fail when the behaviour of the aspect under test deviates from the expected behaviour, yet continue to pass when minor, unrelated changes to the behaviour are made. </p>
<h2><a id="My_first_Hamcrest_test">My first Hamcrest test</h2>
<p>We'll start be writing a very simple JUnit 3 test, but instead of using JUnit's <tt>assertEquals</tt> methods, we use Hamcrest's <tt>assertThat</tt> construct and the standard set of matchers, both of which we statically import: </p>
<pre class="prettyprint"><span class="kwd">import</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="typ">MatcherAssert</span><span class="pun">.</span><span class="pln">assertThat</span><span class="pun">;</span><span class="pln"><br />
</span><span class="kwd">import</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="typ">Matchers</span><span class="pun">.*;</span><span class="pln"><br />
<br />
</span><span class="kwd">import</span><span class="pln"> junit</span><span class="pun">.</span><span class="pln">framework</span><span class="pun">.</span><span class="typ">TestCase</span><span class="pun">;</span><span class="pln"><br />
<br />
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">BiscuitTest</span><span class="pln"> </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">TestCase</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />
&nbsp; </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> testEquals</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />
&nbsp; &nbsp; </span><span class="typ">Biscuit</span><span class="pln"> theBiscuit </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Biscuit</span><span class="pun">(</span><span class="str">"Ginger"</span><span class="pun">);</span><span class="pln"><br />
&nbsp; &nbsp; </span><span class="typ">Biscuit</span><span class="pln"> myBiscuit </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Biscuit</span><span class="pun">(</span><span class="str">"Ginger"</span><span class="pun">);</span><span class="pln"><br />
&nbsp; &nbsp; assertThat</span><span class="pun">(</span><span class="pln">theBiscuit</span><span class="pun">,</span><span class="pln"> equalTo</span><span class="pun">(</span><span class="pln">myBiscuit</span><span class="pun">));</span><span class="pln"><br />
&nbsp; </span><span class="pun">}</span><span class="pln"><br />
</span><span class="pun">}</span></pre>
<p>The <tt>assertThat</tt> method is a stylized sentence for making a test assertion. In this example, the subject of the assertion is the object <tt>biscuit</tt> that is the first method parameter. The second method parameter is a matcher for <tt>Biscuit</tt> objects, here a matcher that checks one object is equal to another using the <tt>Object equals</tt> method. The test passes since the <tt>Biscuit</tt> class defines an equals method. </p>
<p>If you have more than one assertion in your test you can include an identifier for the tested value in the assertion: </p>
<pre class="prettyprint"><span class="pln">assertThat</span><span class="pun">(</span><span class="str">"chocolate chips"</span><span class="pun">,</span><span class="pln"> theBiscuit</span><span class="pun">.</span><span class="pln">getChocolateChipCount</span><span class="pun">(),</span><span class="pln"> equalTo</span><span class="pun">(</span><span class="lit">10</span><span class="pun">));</span><span class="pln"><br />
assertThat</span><span class="pun">(</span><span class="str">"hazelnuts"</span><span class="pun">,</span><span class="pln"> theBiscuit</span><span class="pun">.</span><span class="pln">getHazelnutCount</span><span class="pun">(),</span><span class="pln"> equalTo</span><span class="pun">(</span><span class="lit">3</span><span class="pun">));</span></pre>
<h2><a id="Other_test_frameworks">Other test frameworks</h2>
<p>Hamcrest has been designed from the outset to integrate with different unit testing frameworks. For example, Hamcrest can be used with <a href="http://junit.org/" rel="nofollow">JUnit</a> 3 and 4 and <a href="http://testng.org/" rel="nofollow">TestNG</a>. (For details have a look at the examples that come with the full Hamcrest distribution.) It is easy enough to migrate to using Hamcrest-style assertions in an existing test suite, since other assertion styles can co-exist with Hamcrest's. </p>
<p>Hamcrest can also be used with mock objects frameworks by using adaptors to bridge from the mock objects framework's concept of a matcher to a Hamcrest matcher. For example, <a href="http://jmock.org/" rel="nofollow">JMock</a> 1's <em>constraints</em> are Hamcrest's matchers. Hamcrest provides a JMock 1 adaptor to allow you to use Hamcrest matchers in your JMock 1 tests. JMock 2 doesn't need such an adaptor layer since it is designed to use Hamcrest as its matching library. Hamcrest also provides adaptors for <a href="http://easymock.org/" rel="nofollow">EasyMock</a> 2. Again, see the Hamcrest examples for more details. </p>
<h2><a id="A_tour_of_common_matchers">A tour of common matchers</h2>
<p>Hamcrest comes with a library of useful matchers. Here are some of the most important ones. </p>
<ul>
    <li>Core
    <ul>
        <li><tt>anything</tt> - always matches, useful if you don't care what the object under test is
        <li><tt>describedAs</tt> - decorator to adding custom failure description
        <li><tt>is</tt> - decorator to improve readability - see "Sugar", below </li>
    </ul>
    <li>Logical
    <ul>
        <li><tt>allOf</tt> - matches if all matchers match, short circuits (like Java &amp;&amp;)
        <li><tt>anyOf</tt> - matches if any matchers match, short circuits (like Java ||)
        <li><tt>not</tt> - matches if the wrapped matcher doesn't match and vice versa </li>
    </ul>
    <li>Object
    <ul>
        <li><tt>equalTo</tt> - test object equality using Object.equals
        <li><tt>hasToString</tt> - test Object.toString
        <li><tt>instanceOf</tt>, <tt>isCompatibleType</tt> - test type
        <li><tt>notNullValue</tt>, <tt>nullValue</tt> - test for null
        <li><tt>sameInstance</tt> - test object identity </li>
    </ul>
    <li>Beans
    <ul>
        <li><tt>hasProperty</tt> - test JavaBeans properties </li>
    </ul>
    <li>Collections
    <ul>
        <li><tt>array</tt> - test an array's elements against an array of matchers
        <li><tt>hasEntry</tt>, <tt>hasKey</tt>, <tt>hasValue</tt> - test a map contains an entry, key or value
        <li><tt>hasItem</tt>, <tt>hasItems</tt> - test a collection contains elements
        <li><tt>hasItemInArray</tt> - test an array contains an element </li>
    </ul>
    <li>Number
    <ul>
        <li><tt>closeTo</tt> - test floating point values are close to a given value
        <li><tt>greaterThan</tt>, <tt>greaterThanOrEqualTo</tt>, <tt>lessThan</tt>, <tt>lessThanOrEqualTo</tt> - test ordering </li>
    </ul>
    <li>Text
    <ul>
        <li><tt>equalToIgnoringCase</tt> - test string equality ignoring case
        <li><tt>equalToIgnoringWhiteSpace</tt> - test string equality ignoring differences in runs of whitespace
        <li><tt>containsString</tt>, <tt>endsWith</tt>, <tt>startsWith</tt> - test string matching </li>
    </ul>
    </li>
</ul>
<h2><a id="Sugar">Sugar</h2>
<p>Hamcrest strives to make your tests as readable as possible. For example, the <tt>is</tt> matcher is a wrapper that doesn't add any extra behavior to the underlying matcher. The following assertions are all equivalent: </p>
<pre class="prettyprint"><span class="pln">assertThat</span><span class="pun">(</span><span class="pln">theBiscuit</span><span class="pun">,</span><span class="pln"> equalTo</span><span class="pun">(</span><span class="pln">myBiscuit</span><span class="pun">));</span><span class="pln"><br />
assertThat</span><span class="pun">(</span><span class="pln">theBiscuit</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">is</span><span class="pun">(</span><span class="pln">equalTo</span><span class="pun">(</span><span class="pln">myBiscuit</span><span class="pun">)));</span><span class="pln"><br />
assertThat</span><span class="pun">(</span><span class="pln">theBiscuit</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">is</span><span class="pun">(</span><span class="pln">myBiscuit</span><span class="pun">));</span></pre>
<p>The last form is allowed since <tt>is(T value)</tt> is overloaded to return <tt>is(equalTo(value))</tt>. </p>
<h2><a id="Writing_custom_matchers">Writing custom matchers</h2>
<p>Hamcrest comes bundled with lots of useful matchers, but you'll probably find that you need to create your own from time to time to fit your testing needs. This commonly occurs when you find a fragment of code that tests the same set of properties over and over again (and in different tests), and you want to bundle the fragment into a single assertion. By writing your own matcher you'll elimate code duplication and make your tests more readable! </p>
<p>Let's write our own matcher for testing if a double value has the value NaN (not a number). This is the test we want to write: </p>
<pre class="prettyprint"><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> testSquareRootOfMinusOneIsNotANumber</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />
&nbsp; assertThat</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(-</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">is</span><span class="pun">(</span><span class="pln">notANumber</span><span class="pun">()));</span><span class="pln"><br />
</span><span class="pun">}</span></pre>
<p>And here's the implementation: </p>
<pre class="prettyprint"><span class="kwd">package</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="pln">examples</span><span class="pun">.</span><span class="pln">tutorial</span><span class="pun">;</span><span class="pln"><br />
<br />
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="typ">Description</span><span class="pun">;</span><span class="pln"><br />
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="typ">Factory</span><span class="pun">;</span><span class="pln"><br />
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="typ">Matcher</span><span class="pun">;</span><span class="pln"><br />
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="typ">TypeSafeMatcher</span><span class="pun">;</span><span class="pln"><br />
<br />
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">IsNotANumber</span><span class="pln"> </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">TypeSafeMatcher</span><span class="pun">&lt;</span><span class="typ">Double</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />
<br />
&nbsp; </span><span class="lit">@Override</span><span class="pln"><br />
&nbsp; </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">boolean</span><span class="pln"> matchesSafely</span><span class="pun">(</span><span class="typ">Double</span><span class="pln"> number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />
&nbsp; &nbsp; </span><span class="kwd">return</span><span class="pln"> number</span><span class="pun">.</span><span class="pln">isNaN</span><span class="pun">();</span><span class="pln"><br />
&nbsp; </span><span class="pun">}</span><span class="pln"><br />
<br />
&nbsp; </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> describeTo</span><span class="pun">(</span><span class="typ">Description</span><span class="pln"> description</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />
&nbsp; &nbsp; description</span><span class="pun">.</span><span class="pln">appendText</span><span class="pun">(</span><span class="str">"not a number"</span><span class="pun">);</span><span class="pln"><br />
&nbsp; </span><span class="pun">}</span><span class="pln"><br />
<br />
&nbsp; </span><span class="lit">@Factory</span><span class="pln"><br />
&nbsp; </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Matcher</span><span class="pun">&lt;</span><span class="typ">Double</span><span class="pun">&gt;</span><span class="pln"> notANumber</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />
&nbsp; &nbsp; </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">IsNotANumber</span><span class="pun">();</span><span class="pln"><br />
&nbsp; </span><span class="pun">}</span><span class="pln"><br />
<br />
</span><span class="pun">}</span></pre>
<p>The <tt>assertThat</tt> method is a generic method which takes a <tt>Matcher</tt> parameterized by the type of the subject of the assertion. We are asserting things about <tt>Double</tt> values, so we know that we need a <tt>Matcher&lt;Double&gt;</tt>. For our Matcher implementation it is most convenient to subclass <tt>TypeSafeMatcher</tt>, which does the cast to a <tt>Double</tt> for us. We need only implement the <tt>matchesSafely</tt> method - which simply checks to see if the <tt>Double</tt> is NaN - and the <tt>describeTo</tt> method - which is used to produce a failure message when a test fails. Here's an example of how the failure message looks: </p>
<pre class="prettyprint"><span class="pln">assertThat</span><span class="pun">(</span><span class="lit">1.0</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">is</span><span class="pun">(</span><span class="pln">notANumber</span><span class="pun">()));</span></pre>
<p>fails with the message </p>
<pre class="prettyprint"><span class="pln">java</span><span class="pun">.</span><span class="pln">lang</span><span class="pun">.</span><span class="typ">AssertionError</span><span class="pun">:</span><span class="pln"> <br />
</span><span class="typ">Expected</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> a number<br />
&nbsp; &nbsp; got </span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="lit">1.0</span><span class="pun">&gt;</span></pre>
<p>The third method in our matcher is a convenience factory method. We statically import this method to use the matcher in our test: </p>
<pre class="prettyprint"><span class="kwd">import</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="typ">MatcherAssert</span><span class="pun">.</span><span class="pln">assertThat</span><span class="pun">;</span><span class="pln"><br />
</span><span class="kwd">import</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="typ">Matchers</span><span class="pun">.*;</span><span class="pln"><br />
<br />
</span><span class="kwd">import</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">hamcrest</span><span class="pun">.</span><span class="pln">examples</span><span class="pun">.</span><span class="pln">tutorial</span><span class="pun">.</span><span class="typ">IsNotANumber</span><span class="pun">.</span><span class="pln">notANumber</span><span class="pun">;</span><span class="pln"><br />
<br />
</span><span class="kwd">import</span><span class="pln"> junit</span><span class="pun">.</span><span class="pln">framework</span><span class="pun">.</span><span class="typ">TestCase</span><span class="pun">;</span><span class="pln"><br />
<br />
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">NumberTest</span><span class="pln"> </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">TestCase</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />
<br />
&nbsp; </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> testSquareRootOfMinusOneIsNotANumber</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />
&nbsp; &nbsp; assertThat</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(-</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">is</span><span class="pun">(</span><span class="pln">notANumber</span><span class="pun">()));</span><span class="pln"><br />
&nbsp; </span><span class="pun">}</span><span class="pln"><br />
</span><span class="pun">}</span></pre>
</a>
<img src ="http://www.blogjava.net/liuzheng/aggbug/198642.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-05-06 10:01 <a href="http://www.blogjava.net/liuzheng/articles/198642.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring事务配置笔记(实现不同Service间调用事务) </title><link>http://www.blogjava.net/liuzheng/articles/197097.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 29 Apr 2008 03:58:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/197097.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/197097.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/197097.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/197097.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/197097.html</trackback:ping><description><![CDATA[<strong>一、关键配置示例：</strong> <br />
<span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="sessionFactory"</span><span style="color: rgb(255,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class</span><span style="color: rgb(0,0,255)">="org.springframework.orm.hibernate3.LocalSessionFactoryBean"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="dataSource"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">ref&nbsp;</span><span style="color: rgb(255,0,0)">local</span><span style="color: rgb(0,0,255)">="dataSource"</span><span style="color: rgb(255,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">&nbsp;bean&nbsp;id="transactionManager"&nbsp;class="org.springframework.transaction.jta.JtaTransactionManager"/</span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="transactionManager"</span><span style="color: rgb(255,0,0)">&nbsp;class</span><span style="color: rgb(0,0,255)">="org.springframework.orm.hibernate3.HibernateTransactionManager"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="sessionFactory"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">ref&nbsp;</span><span style="color: rgb(255,0,0)">local</span><span style="color: rgb(0,0,255)">="sessionFactory"</span><span style="color: rgb(255,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">bean</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">&nbsp;Hibernate&nbsp;Transaction&nbsp;Interceptor&nbsp;Definition&nbsp;</span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="hibernateTransactionInterceptor"</span><span style="color: rgb(255,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class</span><span style="color: rgb(0,0,255)">="org.springframework.transaction.interceptor.TransactionInterceptor"</span><span style="color: rgb(255,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parent</span><span style="color: rgb(0,0,255)">="transactionIntercetorTemplate"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="transactionManager"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="hibernateTransactionManager"</span><span style="color: rgb(255,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class</span><span style="color: rgb(0,0,255)">="org.springframework.orm.hibernate3.HibernateTransactionManager"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="sessionFactory"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">ref&nbsp;</span><span style="color: rgb(255,0,0)">bean</span><span style="color: rgb(0,0,255)">="sessionFactory"</span><span style="color: rgb(255,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">bean</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">bean</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">&nbsp;Transction&nbsp;Intercetor's&nbsp;Template&nbsp;</span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="transactionIntercetorTemplate"</span><span style="color: rgb(255,0,0)">&nbsp;abstract</span><span style="color: rgb(0,0,255)">="true"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="transactionAttributes"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">props</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">prop&nbsp;</span><span style="color: rgb(255,0,0)">key</span><span style="color: rgb(0,0,255)">="get*"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">PROPAGATION_REQUIRED,readOnly</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">prop</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">prop&nbsp;</span><span style="color: rgb(255,0,0)">key</span><span style="color: rgb(0,0,255)">="is*"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">PROPAGATION_REQUIRED,readOnly</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">prop</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">prop&nbsp;</span><span style="color: rgb(255,0,0)">key</span><span style="color: rgb(0,0,255)">="check*"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">PROPAGATION_REQUIRED,readOnly</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">prop</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">prop&nbsp;</span><span style="color: rgb(255,0,0)">key</span><span style="color: rgb(0,0,255)">="insert*"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">PROPAGATION_REQUIRED</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">prop</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">prop&nbsp;</span><span style="color: rgb(255,0,0)">key</span><span style="color: rgb(0,0,255)">="update*"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">PROPAGATION_REQUIRED</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">prop</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">prop&nbsp;</span><span style="color: rgb(255,0,0)">key</span><span style="color: rgb(0,0,255)">="delete*"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">PROPAGATION_REQUIRED,-BussException</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">prop</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">prop&nbsp;</span><span style="color: rgb(255,0,0)">key</span><span style="color: rgb(0,0,255)">="*"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">PROPAGATION_REQUIRED</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">prop</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">props</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">bean</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">&nbsp;定义自动代理生成器</span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="autoProxyCreator"</span><span style="color: rgb(255,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class</span><span style="color: rgb(0,0,255)">="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="interceptorNames"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">list</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">idref&nbsp;</span><span style="color: rgb(255,0,0)">bean</span><span style="color: rgb(0,0,255)">="hibernateTransactionInterceptor"</span><span style="color: rgb(255,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">idref&nbsp;</span><span style="color: rgb(255,0,0)">bean</span><span style="color: rgb(0,0,255)">="jdbcTransactionInterceptor"</span><span style="color: rgb(255,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">list</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="beanNames"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">value</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">*Service</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">value</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">&nbsp;<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">list&gt;<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;idref&nbsp;bean="bookService"&nbsp;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list</span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">添加Service&nbsp;bean</span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">bean</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="hibernateDaoTemplate"</span><span style="color: rgb(255,0,0)">&nbsp;abstract</span><span style="color: rgb(0,0,255)">="true"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="sessionFactory"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">ref&nbsp;</span><span style="color: rgb(255,0,0)">bean</span><span style="color: rgb(0,0,255)">="sessionFactory"</span><span style="color: rgb(255,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">bean</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">&nbsp;Dao&nbsp;定义&nbsp;</span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="imageDao"</span><span style="color: rgb(255,0,0)">&nbsp;class</span><span style="color: rgb(0,0,255)">="com.fbyssssite.dao.ImageDao"</span><span style="color: rgb(255,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parent</span><span style="color: rgb(0,0,255)">="hibernateDaoTemplate"</span><span style="color: rgb(255,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="imageFolderDao"</span><span style="color: rgb(255,0,0)">&nbsp;class</span><span style="color: rgb(0,0,255)">="com.fbyssssite.dao.ImageFolderDao"</span><span style="color: rgb(255,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parent</span><span style="color: rgb(0,0,255)">="hibernateDaoTemplate"</span><span style="color: rgb(255,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">/&gt;</span><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">&nbsp;Service&nbsp;定义&nbsp;</span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="imageService"</span><span style="color: rgb(255,0,0)">&nbsp;class</span><span style="color: rgb(0,0,255)">="com.fbyssssite.bo.ImageService"</span><span style="color: rgb(255,0,0)">&nbsp;autowire</span><span style="color: rgb(0,0,255)">="byName"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="dao"</span><span style="color: rgb(255,0,0)">&nbsp;ref</span><span style="color: rgb(0,0,255)">="imageDao"</span><span style="color: rgb(0,0,255)">&gt;&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">bean</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">bean&nbsp;</span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="imageFolderService"</span><span style="color: rgb(255,0,0)">&nbsp;class</span><span style="color: rgb(0,0,255)">="com.fbyssssite.bo.ImageFolderService"</span><span style="color: rgb(255,0,0)">&nbsp;autowire</span><span style="color: rgb(0,0,255)">="byName"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property&nbsp;</span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="dao"</span><span style="color: rgb(255,0,0)">&nbsp;ref</span><span style="color: rgb(0,0,255)">="imageFolderDao"</span><span style="color: rgb(0,0,255)">&gt;&lt;/</span><span style="color: rgb(128,0,0)">property</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">bean</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;</span></span>
<p><strong>二、测试代码：</strong>&nbsp;&nbsp;<br />
</p>
<div style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: windowtext 0.5pt solid; padding-left: 5.4pt; background: rgb(230,230,230) 0% 50%; padding-bottom: 4px; border-left: windowtext 0.5pt solid; width: 95%; padding-top: 4px; border-bottom: windowtext 0.5pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><img id="_70_310_Open_Image" onclick="this.style.display='none'; document.getElementById('_70_310_Open_Text').style.display='none'; document.getElementById('_70_310_Closed_Image').style.display='inline'; document.getElementById('_70_310_Closed_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="_70_310_Closed_Image" style="display: none" onclick="this.style.display='none'; document.getElementById('_70_310_Closed_Text').style.display='none'; document.getElementById('_70_310_Open_Image').style.display='inline'; document.getElementById('_70_310_Open_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif" align="top" /><span style="color: rgb(0,0,255)">public</span><span style="color: rgb(0,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">void</span><span style="color: rgb(0,0,0)">&nbsp;delete(Object&nbsp;entity,IBaseUser&nbsp;user)&nbsp;</span><span style="color: rgb(0,0,255)">throws</span><span style="color: rgb(0,0,0)">&nbsp;BussException&nbsp;</span><span id="_70_310_Closed_Text" style="border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; display: none; border-left: rgb(128,128,128) 1px solid; border-bottom: rgb(128,128,128) 1px solid; background-color: rgb(255,255,255)">...</span><span id="_70_310_Open_Text"><span style="color: rgb(0,0,0)">{<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">super</span><span style="color: rgb(0,0,0)">.delete(entity,&nbsp;user);<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;ImageFolder&nbsp;folder&nbsp;</span><span style="color: rgb(0,0,0)">=</span><span style="color: rgb(0,0,0)">&nbsp;(ImageFolder)entity;<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;String&nbsp;folderId&nbsp;</span><span style="color: rgb(0,0,0)">=</span><span style="color: rgb(0,0,0)">&nbsp;folder.getId();&nbsp;&nbsp;&nbsp;<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;IImageService&nbsp;imageService&nbsp;&nbsp;</span><span style="color: rgb(0,0,0)">=</span><span style="color: rgb(0,0,0)">&nbsp;ServiceFactory.getImageService();<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;imageService.deleteAllInFolder(folderId,user);&nbsp;&nbsp;&nbsp;<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />&nbsp;&nbsp;}</span></span><span style="color: rgb(0,0,0)"><br />
<img id="_394_784_Open_Image" onclick="this.style.display='none'; document.getElementById('_394_784_Open_Text').style.display='none'; document.getElementById('_394_784_Closed_Image').style.display='inline'; document.getElementById('_394_784_Closed_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="_394_784_Closed_Image" style="display: none" onclick="this.style.display='none'; document.getElementById('_394_784_Closed_Text').style.display='none'; document.getElementById('_394_784_Open_Image').style.display='inline'; document.getElementById('_394_784_Open_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif" align="top" />&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">public</span><span style="color: rgb(0,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">void</span><span style="color: rgb(0,0,0)">&nbsp;deleteAllInFolder(String&nbsp;id,IBaseUser&nbsp;sysUser)&nbsp;</span><span style="color: rgb(0,0,255)">throws</span><span style="color: rgb(0,0,0)">&nbsp;BussException&nbsp;</span><span id="_394_784_Closed_Text" style="border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; display: none; border-left: rgb(128,128,128) 1px solid; border-bottom: rgb(128,128,128) 1px solid; background-color: rgb(255,255,255)">...</span><span id="_394_784_Open_Text"><span style="color: rgb(0,0,0)">{(来源:www.iocblog.net)<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;log.info(</span><span style="color: rgb(0,0,0)">"</span><span style="color: rgb(0,0,0)">删除图片夹'</span><span style="color: rgb(0,0,0)">"</span><span style="color: rgb(0,0,0)">+</span><span style="color: rgb(0,0,0)">id</span><span style="color: rgb(0,0,0)">+</span><span style="color: rgb(0,0,0)">"</span><span style="color: rgb(0,0,0)">'中的图片...</span><span style="color: rgb(0,0,0)">"</span><span style="color: rgb(0,0,0)">);<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">throw</span><span style="color: rgb(0,0,0)">&nbsp;</span><span style="color: rgb(0,0,255)">new</span><span style="color: rgb(0,0,0)">&nbsp;BussException(</span><span style="color: rgb(0,0,0)">1</span><span style="color: rgb(0,0,0)">,</span><span style="color: rgb(0,0,0)">"</span><span style="color: rgb(0,0,0)">临时测试：测试事务是否生效的异常。测试完毕后删除此语句。</span><span style="color: rgb(0,0,0)">"</span><span style="color: rgb(0,0,0)">,log);<br />
<img id="_502_777_Open_Image" onclick="this.style.display='none'; document.getElementById('_502_777_Open_Text').style.display='none'; document.getElementById('_502_777_Closed_Image').style.display='inline'; document.getElementById('_502_777_Closed_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="_502_777_Closed_Image" style="display: none" onclick="this.style.display='none'; document.getElementById('_502_777_Closed_Text').style.display='none'; document.getElementById('_502_777_Open_Image').style.display='inline'; document.getElementById('_502_777_Open_Text').style.display='inline';" alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedSubBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;</span><span id="_502_777_Closed_Text" style="border-right: rgb(128,128,128) 1px solid; border-top: rgb(128,128,128) 1px solid; display: none; border-left: rgb(128,128,128) 1px solid; border-bottom: rgb(128,128,128) 1px solid; background-color: rgb(255,255,255)">/**/</span><span id="_502_777_Open_Text"><span style="color: rgb(0,128,0)">/*</span><span style="color: rgb(0,128,0)"><br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;StringBuffer&nbsp;sql&nbsp;=&nbsp;new&nbsp;StringBuffer("delete&nbsp;");<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;sql.append("from&nbsp;");<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;sql.append(getEntityClassName());<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;SqlBuilder&nbsp;sqlBuilder&nbsp;&nbsp;=&nbsp;SqlBuilder.getInstance();<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;sqlBuilder.appendWhereCondition(sql,&nbsp;"folderId='"+id+"'");<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;bulkUpdate(sql.toString(),&nbsp;sysUser);<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">*/</span></span><span style="color: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;<br />
<img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />&nbsp;&nbsp;}</span></span></div>
</div>
<p><br />
<strong>三、测试效果：</strong><br />
抛出BussException之后，imageFolder实体并没有被删除，而是回滚了。<br />
<strong>四、注意事项：</strong><br />
<strong>1.</strong>其中的&nbsp;BussException是一个CheckedException(继承自Exception)，这里不过是一个例子，如果其他方法需要同样的方式处理事务，也要加上-BussException，否则不能回滚；如果是一个UnCheckedException，就不需要在这里配置，默认就会回滚。<br />
<strong>2.</strong>如果采用MySQL，show table status可以查看表的ENGINE类型，MyISAM是不支持事务的，如果需要事务，应该改成InnoDB:alter table [tablename] engine = InnoDB;</p>
<img src ="http://www.blogjava.net/liuzheng/aggbug/197097.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-04-29 11:58 <a href="http://www.blogjava.net/liuzheng/articles/197097.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JMS的官方学习文档</title><link>http://www.blogjava.net/liuzheng/articles/196018.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 25 Apr 2008 09:15:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/196018.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/196018.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/196018.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/196018.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/196018.html</trackback:ping><description><![CDATA[http://java.sun.com/products/jms/tutorial/1_3_1-fcs/doc/jms_tutorialTOC.html
<img src ="http://www.blogjava.net/liuzheng/aggbug/196018.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-04-25 17:15 <a href="http://www.blogjava.net/liuzheng/articles/196018.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>dbunit test 的一个小缺陷</title><link>http://www.blogjava.net/liuzheng/articles/195613.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Thu, 24 Apr 2008 08:00:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/195613.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/195613.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/195613.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/195613.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/195613.html</trackback:ping><description><![CDATA[row 的顺序要有讲究，最新的数据行应该在前面<br />
比如albert2@eln.dev&nbsp;，2是新加的，那么应该在前面，不然要报错<br />
&lt;table name="accountaddress"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;column&gt;address&lt;/column&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;column&gt;accountid&lt;/column&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;row&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;albert2@eln.dev&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;2&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/row&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;row&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;albert@eln.dev&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;2&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/row&gt;<br />
&nbsp;&lt;/table&gt;
<img src ="http://www.blogjava.net/liuzheng/aggbug/195613.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-04-24 16:00 <a href="http://www.blogjava.net/liuzheng/articles/195613.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate 的中文参考文档</title><link>http://www.blogjava.net/liuzheng/articles/195186.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Wed, 23 Apr 2008 09:42:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/195186.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/195186.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/195186.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/195186.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/195186.html</trackback:ping><description><![CDATA[<p align="right">&nbsp;</p>
http://docs.huihoo.com/framework/hibernate/reference-v3_zh-cn/<br />
Hibernate 的中文参考文档
<img src ="http://www.blogjava.net/liuzheng/aggbug/195186.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-04-23 17:42 <a href="http://www.blogjava.net/liuzheng/articles/195186.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>maven2完全使用手册（转）</title><link>http://www.blogjava.net/liuzheng/articles/192164.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 11 Apr 2008 05:26:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/192164.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/192164.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/192164.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/192164.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/192164.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: maven2完全使用手册maven2 起步&nbsp;&nbsp;&nbsp; 相信maven1 大家都已经很熟悉了，具体maven能做什么，就不详细说了。个人觉得maven在开源项目中用的还是比较多的，公司内部，就不太清楚了。我以前的公司用过一段时间，不过后来就没有下文了。&nbsp;&nbsp;&nbsp; 与maven1 相比，maven2可算是几乎重写了，不过从速度来说应该更快...&nbsp;&nbsp;<a href='http://www.blogjava.net/liuzheng/articles/192164.html'>阅读全文</a><img src ="http://www.blogjava.net/liuzheng/aggbug/192164.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-04-11 13:26 <a href="http://www.blogjava.net/liuzheng/articles/192164.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring 事务的处理（转）</title><link>http://www.blogjava.net/liuzheng/articles/192163.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 11 Apr 2008 05:24:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/192163.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/192163.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/192163.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/192163.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/192163.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 本章是第四章的延续，作者向读者展示了如何使用Spring事务管理来保证数据一致性。Spring对事务的管理有丰富的支持，程序控制的和声明式的都有。在本章中，我们会学习到如何把应用程序的代码放置在事务中，以确保在一切顺利时，所有的成果都被固定下来；一旦其中有一步出错，那么整个事情就像没有发生一样。&nbsp;一、理解事务首先我们应该弄清楚什么是事务，这样才能认识到事务的重要性。举一个小小...&nbsp;&nbsp;<a href='http://www.blogjava.net/liuzheng/articles/192163.html'>阅读全文</a><img src ="http://www.blogjava.net/liuzheng/aggbug/192163.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-04-11 13:24 <a href="http://www.blogjava.net/liuzheng/articles/192163.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Date 和 Calender 的转化</title><link>http://www.blogjava.net/liuzheng/articles/190386.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Wed, 02 Apr 2008 07:40:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/190386.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/190386.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/190386.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/190386.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/190386.html</trackback:ping><description><![CDATA[<div id="related_topics" style="position: relative" _eventid="3" _madepositioned="true">&nbsp;</div>
<script type="text/javascript">
            new Draggable("related_topics");
          </script>1.计算某一月份的最大天数 <br />
<br />
Calendar time=Calendar.getInstance(); <br />
time.clear(); <br />
time.set(Calendar.YEAR,year); //year 为 int <br />
time.set(Calendar.MONTH,i-1);//注意,Calendar对象默认一月为0 <br />
int day=time.getActualMaximum(Calendar.DAY_OF_MONTH);//本月份的天数 <br />
注：在使用set方法之前，必须先clear一下，否则很多信息会继承自系统当前时间 <br />
<br />
2.Calendar和<span class="hilite1"><span class="hilite1">Date</span></span>的转化 <br />
<br />
(1) Calendar转化为<span class="hilite1"><span class="hilite1">Date</span></span> <br />
Calendar cal=Calendar.getInstance(); <br />
<span class="hilite1"><span class="hilite1">Date</span></span> <span class="hilite1"><span class="hilite1">date</span></span>=cal.getTime(); <br />
<br />
(2) <span class="hilite1"><span class="hilite1">Date</span></span>转化为Calendar <br />
<span class="hilite1"><span class="hilite1">Date</span></span> <span class="hilite1"><span class="hilite1">date</span></span>=new <span class="hilite1"><span class="hilite1">Date</span></span>(); <br />
Calendar cal=Calendar.getInstance(); <br />
cal.setTime(<span class="hilite1"><span class="hilite1">date</span></span>); <br />
<br />
3.格式化输出日期时间 （这个用的比较多） <br />
<br />
<span class="hilite1"><span class="hilite1">Date</span></span> <span class="hilite1"><span class="hilite1">date</span></span>=new <span class="hilite1"><span class="hilite1">Date</span></span>(); <br />
Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format df=new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("yyyy-MM-dd hh:mm:ss"); <br />
String time=df.format(<span class="hilite1"><span class="hilite1">date</span></span>); <br />
System.out.println(time); <br />
<br />
4.计算一年中的第几星期 <br />
<br />
(1)计算某一天是一年中的第几星期 <br />
Calendar cal=Calendar.getInstance(); <br />
cal.set(Calendar.YEAR, 2006); <br />
cal.set(Calendar.MONTH, 8); <br />
cal.set(Calendar.DAY_OF_MONTH, 3); <br />
int weekno=cal.get(Calendar.WEEK_OF_YEAR); <br />
<br />
(2)计算一年中的第几星期是几号 <br />
Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format df=new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("yyyy-MM-dd"); <br />
Calendar cal=Calendar.getInstance(); <br />
cal.set(Calendar.YEAR, 2006); <br />
cal.set(Calendar.WEEK_OF_YEAR, 1); <br />
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); <br />
System.out.println(df.format(cal.getTime())); <br />
输出: <br />
2006-01-02 <br />
<br />
5.add()和roll()的用法(不太常用) <br />
<br />
(1)add()方法 <br />
Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format df=new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("yyyy-MM-dd"); <br />
Calendar cal=Calendar.getInstance(); <br />
cal.set(Calendar.YEAR, 2006); <br />
cal.set(Calendar.MONTH, 8); <br />
cal.set(Calendar.DAY_OF_MONTH, 3); <br />
cal.add(Calendar.<span class="hilite1"><span class="hilite1">DATE</span></span>, -4); <br />
<span class="hilite1"><span class="hilite1">Date</span></span> <span class="hilite1"><span class="hilite1">date</span></span>=cal.getTime(); <br />
System.out.println(df.format(<span class="hilite1"><span class="hilite1">date</span></span>)); <br />
cal.add(Calendar.<span class="hilite1"><span class="hilite1">DATE</span></span>, 4); <br />
<span class="hilite1"><span class="hilite1">date</span></span>=cal.getTime(); <br />
System.out.println(df.format(<span class="hilite1"><span class="hilite1">date</span></span>)); <br />
输出： <br />
2006-08-30 <br />
2006-09-03 <br />
(2)roll方法 <br />
cal.set(Calendar.YEAR, 2006); <br />
cal.set(Calendar.MONTH, 8); <br />
cal.set(Calendar.DAY_OF_MONTH, 3); <br />
cal.roll(Calendar.<span class="hilite1"><span class="hilite1">DATE</span></span>, -4); <br />
<span class="hilite1"><span class="hilite1">date</span></span>=cal.getTime(); <br />
System.out.println(df.format(<span class="hilite1"><span class="hilite1">date</span></span>)); <br />
cal.roll(Calendar.<span class="hilite1"><span class="hilite1">DATE</span></span>, 4); <br />
<span class="hilite1"><span class="hilite1">date</span></span>=cal.getTime(); <br />
System.out.println(df.format(<span class="hilite1"><span class="hilite1">date</span></span>)); <br />
输出： <br />
2006-09-29 <br />
2006-09-03 <br />
可见，roll()方法在本月内循环，一般使用add()方法； <br />
<br />
6.计算两个任意时间中间的间隔天数（这个比较常用） <br />
(1)传进Calendar对象 <br />
public int getIntervalDays(Calendar startday,Calendar endday)...{ <br />
if(startday.after(endday))...{ <br />
Calendar cal=startday; <br />
startday=endday; <br />
endday=cal; <br />
} <br />
long sl=startday.getTimeInMillis(); <br />
long el=endday.getTimeInMillis(); <br />
<br />
long ei=el-sl; <br />
return (int)(ei/(1000*60*60*24)); <br />
} <br />
(2)传进<span class="hilite1"><span class="hilite1">Date</span></span>对象 <br />
<br />
public int getIntervalDays(<span class="hilite1"><span class="hilite1">Date</span></span> startday,<span class="hilite1"><span class="hilite1">Date</span></span> endday)...{ <br />
if(startday.after(endday))...{ <br />
<span class="hilite1"><span class="hilite1">Date</span></span> cal=startday; <br />
startday=endday; <br />
endday=cal; <br />
} <br />
long sl=startday.getTime(); <br />
long el=endday.getTime(); <br />
long ei=el-sl; <br />
return (int)(ei/(1000*60*60*24)); <br />
} <br />
(3)改进精确计算相隔天数的方法 <br />
public int getDaysBetween (Calendar d1, Calendar d2) ...{ <br />
if (d1.after(d2)) ...{ <br />
java.util.Calendar swap = d1; <br />
d1 = d2; <br />
d2 = swap; <br />
} <br />
int days = d2.get(Calendar.DAY_OF_YEAR) - d1.get(Calendar.DAY_OF_YEAR); <br />
int y2 = d2.get(Calendar.YEAR); <br />
if (d1.get(Calendar.YEAR) != y2) ...{ <br />
d1 = (Calendar) d1.clone(); <br />
do ...{ <br />
days += d1.getActualMaximum(Calendar.DAY_OF_YEAR);//得到当年的实际天数 <br />
d1.add(Calendar.YEAR, 1); <br />
} while (d1.get(Calendar.YEAR) != y2); <br />
} <br />
return days; <br />
} <br />
注意：通过上面的方法可以衍生出求任何时间，如要查出邮箱三周之内收到的邮件（得到当前系统时间－再得到三周前时间）用收件的时间去匹配 最好装化成 long去比较 <br />
如：1年前日期（注意毫秒的转换） <br />
java.util.<span class="hilite1"><span class="hilite1">Date</span></span> my<span class="hilite1"><span class="hilite1">Date</span></span>=new java.util.<span class="hilite1"><span class="hilite1">Date</span></span>(); <br />
long myTime=(my<span class="hilite1"><span class="hilite1">Date</span></span>.getTime()/1000)-60*60*24*365; <br />
my<span class="hilite1"><span class="hilite1">Date</span></span>.setTime(myTime*1000); <br />
String m<span class="hilite1"><span class="hilite1">Date</span></span>=formatter.format(my<span class="hilite1"><span class="hilite1">Date</span></span>); <br />
<br />
7. String 和 <span class="hilite1"><span class="hilite1">Date</span></span> ，Long 之间相互转换 （最常用） <br />
<br />
字符串转化成时间类型（字符串可以是任意类型，只要和Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format中的格式一致即可） <br />
通常我们取时间跨度的时候，会substring出具体时间－－long－比较 <br />
<br />
java.text.Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format sdf = new java.text.Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("M/dd/yyyy hh:mm:ss a",java.util.Locale.US); <br />
java.util.<span class="hilite1"><span class="hilite1">Date</span></span> d = sdf.parse("5/13/2003 10:31:37 AM"); <br />
long dvalue=d.getTime(); <br />
Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format formatter = new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("yyyy-MM-dd HH:mm:ss"); <br />
String m<span class="hilite1"><span class="hilite1">Date</span></span>Time1=formatter.format(d); <br />
<br />
8. 通过时间求时间 <br />
<br />
年月周求日期 <br />
Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format formatter2 = new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("yyyy-MM F E"); <br />
java.util.<span class="hilite1"><span class="hilite1">Date</span></span> <span class="hilite1"><span class="hilite1">date</span></span>2= formatter2.parse("2003-05 5 星期五"); <br />
Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format formatter3 = new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("yyyy-MM-dd"); <br />
String my<span class="hilite1"><span class="hilite1">date</span></span>2=formatter3.format(<span class="hilite1"><span class="hilite1">date</span></span>2); <br />
<br />
求是星期几 <br />
my<span class="hilite1"><span class="hilite1">date</span></span>= myFormatter.parse("2001-1-1"); <br />
Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format formatter4 = new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("E"); <br />
String my<span class="hilite1"><span class="hilite1">date</span></span>3=formatter4.format(my<span class="hilite1"><span class="hilite1">date</span></span>); <br />
<br />
9. java 和 具体的数据库结合 <br />
<br />
在开发web应用中，针对不同的数据库日期类型，我们需要在我们的程序中对日期类型做各种不同的转换。若对应数据库数据是oracle的<span class="hilite1"><span class="hilite1">Date</span></span>类型，即只需要年月日的，可以选择使用java.sql.<span class="hilite1"><span class="hilite1">Date</span></span>类型，若对应的是MSsqlserver 数据库的<span class="hilite1"><span class="hilite1">Date</span></span>Time类型，即需要年月日时分秒的，选择java.sql.Timestamp类型 <br />
你可以使用<span class="hilite1"><span class="hilite1">date</span></span>Format定义时间日期的格式，转一个字符串即可 <br />
<br />
class <span class="hilite1"><span class="hilite1">Date</span></span>test{ <br />
*method 将字符串类型的日期转换为一个timestamp（时间戳记java.sql.Timestamp） <br />
*@param <span class="hilite1"><span class="hilite1">date</span></span>String 需要转换为timestamp的字符串 <br />
*@return dataTime timestamp <br />
<br />
public final static java.sql.Timestamp string2Time(String <span class="hilite1"><span class="hilite1">date</span></span>String) <br />
throws java.text.ParseException { <br />
<span class="hilite1"><span class="hilite1">Date</span></span>Format <span class="hilite1"><span class="hilite1">date</span></span>Format; <br />
<span class="hilite1"><span class="hilite1">date</span></span>Format = new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("yyyy-MM-dd kk:mm:ss.SSS", Locale.ENGLISH);//设定格式 <br />
//<span class="hilite1"><span class="hilite1">date</span></span>Format = new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("yyyy-MM-dd kk:mm:ss", Locale.ENGLISH); <br />
<span class="hilite1"><span class="hilite1">date</span></span>Format.setLenient(false); <br />
java.util.<span class="hilite1"><span class="hilite1">Date</span></span> time<span class="hilite1"><span class="hilite1">Date</span></span> = <span class="hilite1"><span class="hilite1">date</span></span>Format.parse(<span class="hilite1"><span class="hilite1">date</span></span>String);//util类型 <br />
java.sql.Timestamp <span class="hilite1"><span class="hilite1">date</span></span>Time = new java.sql.Timestamp(time<span class="hilite1"><span class="hilite1">Date</span></span>.getTime());//Timestamp类型,time<span class="hilite1"><span class="hilite1">Date</span></span>.getTime()返回一个long型 <br />
return <span class="hilite1"><span class="hilite1">date</span></span>Time; <br />
} <br />
<br />
*method 将字符串类型的日期转换为一个<span class="hilite1"><span class="hilite1">Date</span></span>（java.sql.<span class="hilite1"><span class="hilite1">Date</span></span>） <br />
*@param <span class="hilite1"><span class="hilite1">date</span></span>String 需要转换为<span class="hilite1"><span class="hilite1">Date</span></span>的字符串 <br />
*@return dataTime <span class="hilite1"><span class="hilite1">Date</span></span> <br />
<br />
public final static java.sql.<span class="hilite1"><span class="hilite1">Date</span></span> string2<span class="hilite1"><span class="hilite1">Date</span></span>(String <span class="hilite1"><span class="hilite1">date</span></span>String) <br />
throws java.lang.Exception { <br />
<span class="hilite1"><span class="hilite1">Date</span></span>Format <span class="hilite1"><span class="hilite1">date</span></span>Format; <br />
<span class="hilite1"><span class="hilite1">date</span></span>Format = new Simple<span class="hilite1"><span class="hilite1">Date</span></span>Format("yyyy-MM-dd", Locale.ENGLISH); <br />
<span class="hilite1"><span class="hilite1">date</span></span>Format.setLenient(false); <br />
java.util.<span class="hilite1"><span class="hilite1">Date</span></span> time<span class="hilite1"><span class="hilite1">Date</span></span> = <span class="hilite1"><span class="hilite1">date</span></span>Format.parse(<span class="hilite1"><span class="hilite1">date</span></span>String);//util类型 <br />
java.sql.<span class="hilite1"><span class="hilite1">Date</span></span> <span class="hilite1"><span class="hilite1">date</span></span>Time = new java.sql.<span class="hilite1"><span class="hilite1">Date</span></span>(time<span class="hilite1"><span class="hilite1">Date</span></span>.getTime());//sql类型 <br />
return <span class="hilite1"><span class="hilite1">date</span></span>Time; <br />
} <br />
<br />
public static void main(String[] args){ <br />
<span class="hilite1"><span class="hilite1">Date</span></span> da = new <span class="hilite1"><span class="hilite1">Date</span></span>(); <br />
注意：这个地方da.getTime()得到的是一个long型的值 <br />
System.out.println(da.getTime()); <br />
<br />
由日期<span class="hilite1"><span class="hilite1">date</span></span>转换为timestamp <br />
<br />
第一种方法：使用new Timestamp(long) <br />
Timestamp t = new Timestamp(new <span class="hilite1"><span class="hilite1">Date</span></span>().getTime()); <br />
System.out.println(t); <br />
<br />
第二种方法：使用Timestamp(int year,int month,int <span class="hilite1"><span class="hilite1">date</span></span>,int hour,int minute,int second,int nano) <br />
Timestamp tt = new Timestamp(Calendar.getInstance().get( <br />
Calendar.YEAR) - 1900, Calendar.getInstance().get( <br />
Calendar.MONTH), Calendar.getInstance().get( <br />
Calendar.<span class="hilite1"><span class="hilite1">DATE</span></span>), Calendar.getInstance().get( <br />
Calendar.HOUR), Calendar.getInstance().get( <br />
Calendar.MINUTE), Calendar.getInstance().get( <br />
Calendar.SECOND), 0); <br />
System.out.println(tt); <br />
<br />
try { <br />
String sTo<span class="hilite1"><span class="hilite1">Date</span></span> = "2005-8-18";//用于转换成java.sql.<span class="hilite1"><span class="hilite1">Date</span></span>的字符串 <br />
String sToTimestamp = "2005-8-18 14:21:12.123";//用于转换成java.sql.Timestamp的字符串 <br />
<span class="hilite1"><span class="hilite1">Date</span></span> <span class="hilite1"><span class="hilite1">date</span></span>1 = string2<span class="hilite1"><span class="hilite1">Date</span></span>(sTo<span class="hilite1"><span class="hilite1">Date</span></span>); <br />
Timestamp <span class="hilite1"><span class="hilite1">date</span></span>2 = string2Time(sToTimestamp); <br />
System.out.println("<span class="hilite1"><span class="hilite1">Date</span></span>:"+<span class="hilite1"><span class="hilite1">date</span></span>1.toString());//结果显示 <br />
System.out.println("Timestamp:"+<span class="hilite1"><span class="hilite1">date</span></span>2.toString());//结果显示 <br />
}catch(Exception e) { <br />
e.printStackTrace(); <br />
} <br />
} <br />
} 
<img src ="http://www.blogjava.net/liuzheng/aggbug/190386.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-04-02 15:40 <a href="http://www.blogjava.net/liuzheng/articles/190386.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DbUnit入门实战(转)</title><link>http://www.blogjava.net/liuzheng/articles/190128.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 01 Apr 2008 08:30:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/190128.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/190128.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/190128.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/190128.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/190128.html</trackback:ping><description><![CDATA[<strong>DbUnit入门实战<br />
<br />
</strong><br />
相信做过单元测试的人都会对JUnit非常的熟悉了，今天要介绍的DbUnit(http://dbunit.sourceforge.net/)则是专门针对数据库测试的对JUnit的一个扩展，它可以将测试对象数据库置于一个测试轮回之间的状态。鉴于目前国内介绍DbUnit的系统教程比较少见，本文将分从理论和实例两个方面带你领略DbUnit的精彩世界。<br />
<br />
DbUnit设计理念<br />
熟悉单元测试的开发人员都知道，在对数据库进行单元测试时候，通常采用的方案有运用模拟对象(mock objects)和stubs两种。通过隔离关联的数据库访问类，比如JDBC的相关操作类，来达到对数据库操作的模拟测试。然而某些特殊的系统，比如利用了EJB的CMP(container-managed persistence)的系统，数据库的访问对象是在最底层而且很隐蔽的，那么这两种解决方案对这些系统就显得力不从心了。<br />
<br />
DBUnit的设计理念就是在测试之前，备份数据库，然后给对象数据库植入我们需要的准备数据，最后，在测试完毕后，读入备份数据库，回溯到测试前的状态；<br />
而且又因为DBUnit是对JUnit的一种扩展，开发人员可以通过创建测试用例代码，在这些测试用例的生命周期内来对数据库的操作结果进行比较。<br />
<br />
DbUnit测试基本概念和流程<br />
基于DbUnit 的测试的主要接口是IDataSet。IDataSet代表一个或多个表的数据。<br />
可以将数据库模式的全部内容表示为单个IDataSet 实例。这些表本身由Itable 实例来表示。<br />
IDataSet 的实现有很多，每一个都对应一个不同的数据源或加载机制。最常用的几种 IDataSet实现为： <br />
FlatXmlDataSet：数据的简单平面文件 XML 表示 <br />
QueryDataSet：用 SQL 查询获得的数据 <br />
DatabaseDataSet：数据库表本身内容的一种表示 <br />
XlsDataSet ：数据的excel表示<br />
<br />
一般而言，使用DbUnit进行单元测试的流程如下：<br />
1 根据业务，做好测试用的准备数据和预想结果数据，通常准备成xml格式文件。<br />
2 在setUp()方法里边备份数据库中的关联表。<br />
3 在setUp()方法里边读入准备数据。<br />
4 对测试类的对应测试方法进行实装:执行对象方法，把数据库的实际执行结果和预想结果进行比较。<br />
5 在tearDown()方法里边,把数据库还原到测试前状态。<br />
<p>DbUnit开发实例<br />
下面通过一个实例来说明DbUnit的实际运用。<br />
<br />
实例准备<br />
比如有一个学生表[student]，结构如下：<br />
<hr />
id char(4) pk 学号<br />
name char(50) 姓名<br />
sex char(1) 性别<br />
birthday date 出生日期<br />
<hr />
准备数据如下：<br />
<hr />
id name sex birthday<br />
0001 翁仔 m 1979-12-31<br />
0002 王翠花 f 1982-08-09
<hr />
测试对象类为StudentOpe.java，里边有2个方法：<br />
findStudent(String id) :根据主键id找记录<br />
addStudent(Student student) ：添加一条记录<br />
<br />
在测试addStudent方法时候，我们准备添加如下一条数据<br />
<hr />
id name sex birthday<br />
0088 王耳朵 m 1982-01-01<br />
<hr />
那么在执行该方法后，数据库的student表里的数据是这样的：<br />
<hr />
id name sex birthday<br />
0001 翁仔 m 1979-12-31<br />
0002 王翠花 f 1982-08-09 <br />
0088 王耳朵 m 1982-01-01<br />
<hr />
<p>然后我们说明如何对这2个方法进行单元测试。<br />
<br />
实例展开<br />
1 把准备数据和预想数据转换成xml文件<br />
student_pre.xml<br />
<hr />
&lt;?xml version='1.0' encoding="gb2312"?&gt;<br />
&lt;dataset&gt;<br />
&lt;student id="0001" name="翁仔" sex="m" birthday="1979-12-31"/&gt;<br />
&lt;student id="0002" name="王翠花" sex="f" birthday="1982-08-09"/&gt;<br />
&lt;/dataset&gt;<br />
<hr />
<br />
student_exp.xml<br />
<hr />
&lt;?xml version='1.0' encoding="gb2312"?&gt;<br />
&lt;dataset&gt;<br />
&lt;student id="0001" name="翁仔" sex="m" birthday="1979-12-31"/&gt;<br />
&lt;student id="0002" name="王翠花" sex="f" birthday="1982-08-09"/&gt;<br />
&lt;student id="0088" name="王耳朵" sex="m" birthday="1982-01-01"/&gt;<br />
&lt;/dataset&gt;<br />
<hr />
<p>2 实装setUp方法，详细见代码注释。<br />
<hr />
protected void setUp() {<br />
IDatabaseConnection connection =null;<br />
try{<br />
super.setUp();<br />
//本例使用postgresql数据库 <br />
Class.forName("org.postgresql.Driver");<br />
//连接DB <br />
Connection conn=DriverManager.getConnection("jdbc:postgresql:testdb.test","postgres","postgres");<br />
//获得DB连接<br />
connection =new DatabaseConnection(conn);<br />
<br />
//对数据库中的操作对象表student进行备份<br />
QueryDataSet backupDataSet = new QueryDataSet(connection);<br />
backupDataSet.addTable("student");<br />
file=File.createTempFile("student_back",".xml");//备份文件<br />
FlatXmlDataSet.write(backupDataSet,new FileOutputStream(file));<br />
<br />
//准备数据的读入<br />
IDataSet dataSet = new FlatXmlDataSet( new FileInputStream("student_pre.xml"));<br />
DatabaseOperation.CLEAN_INSERT.execute(connection,dataSet);<br />
<br />
}catch(Exception e){<br />
e.printStackTrace();<br />
}finally{<br />
try{<br />
if(connection!=null) connection.close();<br />
}catch(SQLException e){}<br />
}<br />
}<br />
<hr />
3 实装测试方法，详细见代码注释。<br />
<br />
*检索类方法，可以利用assertEquals()方法，拿表的字段进行比较。<br />
<hr />
// findStudent <br />
public void testFindStudent() throws Exception{<br />
//执行findStudent方法<br />
StudentOpe studentOpe=new StudentOpe();<br />
Student result = studentOpe.findStudent("0001");<br />
<br />
//预想结果和实际结果的比较<br />
assertEquals("翁仔",result.getName());<br />
assertEquals("m",result.getSex());<br />
assertEquals("1979-12-31",result.getBirthDay());<br />
}<br />
<hr />
<br />
*更新，添加，删除等方法，可以利用Assertion.assertEquals()方法，拿表的整体来比较。<br />
<hr />
public void testAddStudent() throws Exception{<br />
//执行addStudent方法<br />
StudentOpe studentOpe=new StudentOpe();<br />
//被追加的记录<br />
Student newStudent = new Student("0088","王耳朵","m","1982-01-01");<br />
//执行追加方法 <br />
Student result = studentOpe.addStudent(newStudent);<br />
<br />
//预想结果和实际结果的比较<br />
IDatabaseConnection connection=null;<br />
<br />
try{<br />
<br />
//预期结果取得<br />
IDataSet expectedDataSet = new FlatXmlDataSet(new FileInputStream("student_exp.xml"));<br />
ITable expectedTable = expectedDataSet.getTable("student");<br />
<br />
//实际结果取得<br />
Connection conn=getConnection();<br />
connection =new DatabaseConnection(conn);<br />
<br />
IDataSet databaseDataSet = connection.createDataSet();<br />
ITable actualTable = databaseDataSet.getTable("student");<br />
<br />
//比较<br />
Assertion.assertEquals(expectedTable, actualTable);<br />
<br />
}finally{<br />
if(connection!=null) connection.close();<br />
}<br />
}<br />
<hr />
<br />
*如果在整体比较表的时候，有个别字段不需要比较，可以用DefaultColumnFilter.excludedColumnsTable()方法，<br />
将指定字段给排除在比较范围之外。比如上例中不需要比较birthday这个字段的话，那么可以如下代码所示进行处理：<br />
<hr />
ITable filteredExpectedTable = DefaultColumnFilter.excludedColumnsTable(expectedTable, new String[]{"birthday"});<br />
ITable filteredActualTable = DefaultColumnFilter.excludedColumnsTable(actualTable,new String[]{"birthday"});<br />
Assertion.assertEquals(filteredExpectedTable, filteredActualTable);<br />
<hr />
<br />
4 在tearDown()方法里边,把数据库还原到测试前状态<br />
<hr />
protected void tearDown() throws Exception{<br />
<br />
IDatabaseConnection connection =null;<br />
try{<br />
super.tearDown();<br />
Connection conn=getConnection();<br />
connection =new DatabaseConnection(conn);<br />
<br />
IDataSet dataSet = new FlatXmlDataSet(file);<br />
DatabaseOperation.CLEAN_INSERT.execute(connection,dataSet);<br />
<br />
}catch(Exception e){<br />
e.printStackTrace();<br />
}finally{<br />
try{<br />
if(connection!=null) connection.close();<br />
}catch(SQLException e){}<br />
}<br />
<br />
}<br />
<hr />
<br />
最后<br />
无疑，使用DbUnit能够极大的提高数据库测试的效率，希望通过本文能够让您掌握这一数据库测试的利器
<img src ="http://www.blogjava.net/liuzheng/aggbug/190128.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-04-01 16:30 <a href="http://www.blogjava.net/liuzheng/articles/190128.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学习正则表达式的很好的网站</title><link>http://www.blogjava.net/liuzheng/articles/186269.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Fri, 14 Mar 2008 06:08:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/186269.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/186269.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/186269.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/186269.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/186269.html</trackback:ping><description><![CDATA[http://regexlib.com/RETester.aspx<br />
http://www.regexlib.com/Default.aspx<br />
<img src ="http://www.blogjava.net/liuzheng/aggbug/186269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-03-14 14:08 <a href="http://www.blogjava.net/liuzheng/articles/186269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用log4j记录错误信息，并向指定邮箱中发邮件</title><link>http://www.blogjava.net/liuzheng/articles/182763.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Thu, 28 Feb 2008 10:02:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/182763.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/182763.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/182763.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/182763.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/182763.html</trackback:ping><description><![CDATA[在log4j的properties中配置一下属性：<br />
log4j.appender.mail=org.apache.log4j.net.SMTPAppender<br />
log4j.appender.mail.Threshold=ERROR<br />
log4j.appender.mail.BufferSize=10<br />
log4j.appender.mail.To=bastiaan.bakker@enovation.nl,simon.harinck@enovation.nl,albert.vthart@enovation.nl,marin.tapnoi@enovation.nl<br />
log4j.appender.mail.From=helpdesk@enovation.nl<br />
log4j.appender.mail.SMTPHost=eserver1.oper.enovation.net<br />
log4j.appender.mail.Subject=Scheduler Error Log Message<br />
log4j.appender.mail.layout=org.apache.log4j.PatternLayout<br />
log4j.appender.mail.layout.ConversionPattern=%d [%t] %-5p %c - %m%n<br />
<br />
<br />
这样配置后，如果log.error运行，就会向指定的邮箱中发邮件
<img src ="http://www.blogjava.net/liuzheng/aggbug/182763.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-02-28 18:02 <a href="http://www.blogjava.net/liuzheng/articles/182763.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式30分钟入门教程(转)</title><link>http://www.blogjava.net/liuzheng/articles/180658.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 19 Feb 2008 06:04:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/180658.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/180658.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/180658.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/180658.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/180658.html</trackback:ping><description><![CDATA[<h2 id="mission">本文目标</h2>
<p>30分钟内让你明白正则表达式是什么，并对它有一些基本的了解，让你可以在自己的程序或网页里使用它。</p>
<h2 id="howtouse">如何使用本教程</h2>
<p class="important">最重要的是——请给我<em>30分钟</em>，如果你没有使用正则表达式的经验，请不要试图在30<em>秒</em>内入门。当然，如果你是超人，那自然得另当别论。</p>
<p>别被下面那些复杂的表达式吓倒，只要跟着我一步一步来，你会发现正则表达式其实并<span lang="zh-cn">没有</span>你想像中的那么困难。当然，如果你看完了这篇教程之后，发现自己明白了很多，却又几乎什么都记不得，那也是很正常的——我认为，没接触过正则表达式的人在看完这篇教程后，能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本的原理，以后你还需要多练习，多使用，才能熟练掌握正则表达式。</p>
<p>除了作为入门教程之外，本文还试图成为可以在日常工作中使用的正则表达式语法参考手册。就作者本人的经历来说，这个目标还是完成得不错的——你看，我自己也没能把所有的东西记下来，不是吗？</p>
<p>文本格式约定：<span class="name">专业术语</span>&nbsp;<span class="code">元字符/语法格式</span>&nbsp;<span class="regex">正则表达式</span>&nbsp;<span class="part">正则表达式中的一部分(用于分析)</span>&nbsp;<span class="string">用于在其中搜索的字符串</span>&nbsp;<span class="desc">对正则表达式或其中一部分的说明</span><a id="clearButton" accesskey="c" onclick="return clearFormats();" href="http://www.unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm">清除格式</a></p>
<h2 id="introduction">正则表达式到底是什么？</h2>
<p>在编写处理字符串的程序或网页时，经常会有查找符合某些复杂规则的字符串的需要。<span class="name">正则表达式</span>就是用于描述这些规则的工具。换句话说，正则表达式就是记录文本规则的代码。</p>
<p>很可能你使用过Windows/Dos下用于文件查找的<span class="name">通配符(wildcard)</span>，也就是<span class="code">*</span>和<span class="code">?</span>。如果你想查找某个目录下的所有的Word文档的话，你会搜索<span style="color: red">*.doc</span>。在这里，<span class="code">*</span>会被解释成任意的<a title="参考" href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#reference">字符串</a>。和通配符类似，正则表达式也是用来进行<a title="参考" href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#reference">文本</a><a title="参考" href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#reference">匹配</a>的工具，只不过比起通配符，它能更精确地描述你的需求——当然，代价就是更复杂——比如你可以编写一个正则表达式，用来查找<span class="desc">所有以0开头，后面跟着2-3个数字，然后是一个连字号&#8220;-&#8221;，最后是7或8位数字的字符串</span>(像<span class="string">010-12345678</span>或<span class="string">0376-7654321</span>)。</p>
<p id="match">正则表达式是用于进行文本匹配的工具，所以本文里多次提到了在字符串里搜索/查找，这种说法的意思是在给定的字符串中，寻找与给定的正则表达式相匹配的部分。有可能字符串里有不止一个部分满足给定的正则表达式，这时每一个这样的部分被称为一个匹配。<span class="name">匹配</span>在本文里可能会有三种意思：一种是形容词性的，比如说一个字符串匹配一个表达式；一种是动词性的，比如说在字符串里匹配正则表达式；还有一种是名词性的，就是刚刚说到的&#8220;字符串中满足给定的正则表达式的一部分&#8221;。</p>
<h2 id="getstarted">入门</h2>
<p>学习正则表达式的最好方法是从例子开始，理解例子之后再自己对例子进行修改，实验。下面给出了不少简单的例子，并对它们作了详细的说明。</p>
<p>假设你在一篇英文小说里查找<span class="desc">hi</span>，你可以使用正则表达式<span class="regex">hi</span>。</p>
<p>这是最简单的正则表达式了，它可以精确匹配这样的字符串：<span class="desc">由两个字符组成，前一个字符是h,后一个是i</span>。通常，处理正则表达式的工具会提供一个忽略大小写的选项，如果选中了这个选项，它可以匹配<span class="string">hi</span>,<span class="string">HI</span>,<span class="string">Hi</span>,<span class="string">hI</span>这四种情况中的任意一种。</p>
<p>不幸的是，很多单词里包含<span class="string">hi</span>这两个连续的字符，比如<span class="string">him</span>,<span class="string">history</span>,<span class="string">high</span>等等。用<span class="regex">hi</span>来查找的话，这里边的<span class="string">hi</span>也会被找出来。如果要<span class="desc">精确地查找hi这个单词</span>的话，我们应该使用<span class="regex">\bhi\b</span>。</p>
<p><span class="part">\b</span>是正则表达式规定的一个特殊代码（好吧，某些人叫它<span class="name">元字符，metacharacter</span>），代表着<span class="desc">单词的开头或结尾，也就是单词的分界处</span>。虽然通常英文的单词是由空格或标点符号或换行来分隔的，但是<span class="code">\b</span>并不匹配这些单词分隔符中的任何一个，它<strong>只匹配一个位置</strong>。（如果需要更精确的说法，<span class="code">\b</span>匹配这样的位置：它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)<span class="code">\w</span>）</p>
<p>假如你要找的是<span class="desc">hi后面不远处跟着一个Lucy</span>，你应该用<span class="regex">\bhi\b.*\bLucy\b</span>。</p>
<p>这里，<span class="part">.</span>是另一个元字符，匹配<span class="desc">除了换行符以外的任意字符</span>。<span class="part">*</span>同样是元字符，不过它代表的不是字符，也不是位置，而是数量——它指定*<span class="desc">前边的内容可以连续重复出现任意次以使整个表达式得到匹配</span>。因此，<span class="part">.*</span>连在一起就意味着<span class="desc">任意数量的不包含换行的字符</span>。现在<span class="regex">\bhi\b.*\bLucy\b</span>的意思就很明显了：<span class="desc">先是一个单词hi,然后是任意个任意字符(但不能是换行)，最后是Lucy这个单词</span>。</p>
<p>如果同时使用其它的一些元字符，我们就能构造出功能更强大的正则表达式。比如下面这个例子：</p>
<p><span class="regex">0\d\d-\d\d\d\d\d\d\d\d</span>匹配这样的字符串：<span class="desc">以0开头，然后是两个数字，然后是一个连字号&#8220;-&#8221;，最后是8个数字</span>(也就是中国的电话号码。当然，这个例子只能匹配区号为3位的情形)。</p>
<p>这里的<span class="part">\d</span>是一个新的元字符，匹配<span class="desc">任意的数字(0，或1，或2，或&#8230;&#8230;)</span>。<span class="part">-</span>不是元字符，只匹配它本身——连字号。</p>
<p>为了避免那么多烦人的重复，我们也可以这样写这个表达式：<span class="regex">0\d{2}-\d{8}</span>。 这里<span class="part">\d</span>后面的<span class="part">{2}</span>(<span class="part">{8}</span>)的意思是前面<span class="part">\d</span><span class="desc">必须连续重复匹配2次(8次)</span>。</p>
<h2 id="testing">测试正则表达式</h2>
<p>如果你不觉得正则表达式很难读写的话，要么你是一个天才，要么，你不是地球人。正则表达式的语法很令人头疼，即使对经常使用它的人来说也是如此。由于难于读写，容易出错，所以很有必要创建一种工具来测试正则表达式。</p>
<p>由于在不同的环境下正则表达式的一些细节是不相同的，本教程介绍的是Microsoft .Net 2.0下正则表达式的行为，所以，我向你介绍一个.Net下的工具<a title="转到RegexTester的官方网站（英文）" href="http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,13bce26d-7755-441e-92b3-1eb5f9e859f9.aspx">Regex Tester</a>。首先你确保已经安装了<a title="转到下载.Net Framework 2.0的页面" href="http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&amp;FamilyID=0856eacb-4362-4b0d-8edd-aab15c5e04f5">.Net Framework 2.0</a>，然后<a title="从www.unibetter.com下载Regex Tester, 75KB" href="http://www.unibetter.com/deerchao/downloads/RegexTester.zip">下载Regex Tester</a>。这是个绿色软件，下载完后打开压缩包,直接运行RegexTester.exe就可以了。</p>
<p>下面是Regex Tester运行时的截图：</p>
<p><img alt="Regex Tester运行时的截图" src="http://unibetter.com/deerchao/images/RegexTester.jpg" /></p>
<h2 id="metacode">元字符</h2>
<p>现在你已经知道几个很有用的元字符了，如<span class="code">\b</span>,<span class="code">.</span>,<span class="code">*</span>，还有<span class="code">\d</span>.当然还有更多的元字符可用，比如<span class="code">\s</span>匹配<span class="desc">任意的空白符，包括空格，制表符(Tab)，换行符，中文全角空格等</span>。<span class="code">\w</span>匹配<span class="desc">字母或数字或下划线或汉字等</span>。</p>
<p>下面来试试更多的例子：</p>
<p><span class="regex">\ba\w*\b</span>匹配<span class="desc">以字母<span class="part">a</span>开头的单词——先是某个单词开始处(<span class="part">\b</span>)，然后是字母<span class="part">a</span>,然后是任意数量的字母或数字(<span class="part">\w*</span>)，最后是单词结束处(<span class="part">\b</span>)</span>（好吧，现在我们说说正则表达式里的单词是什么意思吧：就是几个连续的<span class="code">\w</span>。不错，这与学习英文时要背的成千上万个同名的东西的确关系不大）。</p>
<p><span class="regex">\d+</span>匹配<span class="desc">1个或更多连续的数字</span>。这里的<span class="part">+</span>是和<span class="code">*</span>类似的元字符，不同的是<span class="code">*</span>匹配<span class="desc">重复任意次(可能是0次)</span>，而<span class="code">+</span>则匹配<span class="desc">重复1次或更多次</span>。</p>
<p><span class="regex">\b\w{6}\b</span> 匹配<span class="desc">刚好6个字母/数字的单词</span>。</p>
<table cellspacing="0">
    <caption>表1.常用的元字符</caption>
    <tbody>
        <tr>
            <th>代码</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><span class="code">.</span></td>
            <td><span class="desc">匹配除换行符以外的任意字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\w</span></td>
            <td><span class="desc">匹配字母或数字或下划线或汉字</span></td>
        </tr>
        <tr>
            <td><span class="code">\s</span></td>
            <td><span class="desc">匹配任意的空白符</span></td>
        </tr>
        <tr>
            <td><span class="code">\d</span></td>
            <td><span class="desc">匹配数字</span></td>
        </tr>
        <tr>
            <td><span class="code">\b</span></td>
            <td><span class="desc">匹配单词的开始或结束</span></td>
        </tr>
        <tr>
            <td><span class="code">^</span></td>
            <td><span class="desc">匹配字符串的开始</span></td>
        </tr>
        <tr>
            <td><span class="code">$</span></td>
            <td><span class="desc">匹配字符串的结束</span></td>
        </tr>
    </tbody>
</table>
<p>元字符<span class="code">^</span>（和数字6在同一个键位上的符号）以及<span class="code">$</span>和<span class="code">\b</span>有点类似，都匹配一个位置。<span class="code">^</span>匹配你要用来查找的字符串的开头，<span class="code">$</span>匹配结尾。这两个代码在验证输入的内容时非常有用，比如一个网站如果要求你填写的QQ号必须为5位到12位数字时，可以使用：<span class="regex">^\d{5,12}$</span>。</p>
<p>这里的<span class="part">{5,12}</span>和前面介绍过的<span class="part">{2}</span>是类似的，只不过<span class="part">{2}</span>匹配<span class="desc">只能不多不少重复2次</span>，<span class="part">{5,12}</span>则是<span class="desc">重复的次数不能少于5次，不能多于12次</span>，否则都不匹配。</p>
<p>因为使用了<span class="part">^</span>和<span class="part">$</span>，所以输入的整个字符串都要用来和<span class="part">\d{5,12}</span>来匹配，也就是说整个输入<span class="desc">必须是5到12个数字</span>，因此如果输入的QQ号能匹配这个正则表达式的话，那就符合要求了。</p>
<p>和忽略大小写的选项类似，有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项，<span class="code">^</span>和<span class="code">$</span>的意义就变成了<span class="desc">匹配行的开始处和结束处</span>。</p>
<h2 id="escape">字符转义</h2>
<p>如果你想查找元字符本身的话，比如你查找<span class="desc">.</span>,或者<span class="desc">*</span>,就出现了问题：你没法指定它们，因为它们会被解释成其它的意思。这时你就必须使用<span class="code">\</span>来取消这些字符的特殊意义。因此，你应该使用<span class="regex">\.</span>和<span class="regex">\*</span>。当然，要查找<span class="desc">\</span>本身，你也得用<span class="regex">\\</span>.</p>
<p>例如：<span class="regex">www\.unibetter\.com</span>匹配<span class="desc">www.unibetter.com</span>，<span class="regex">c:\\Windows</span>匹配<span class="desc">c:\Windows</span>。</p>
<h2 id="repeat">重复</h2>
<p>你已经看过了前面的<span class="code">*</span>,<span class="code">+</span>,<span class="code">{2}</span>,<span class="code">{5,12}</span>这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码，例如*,{5,12}等)：</p>
<table cellspacing="0">
    <caption>表2.常用的限定符</caption>
    <tbody>
        <tr>
            <th>代码/语法</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><span class="code">*</span></td>
            <td><span class="desc">重复零次或更多次</span></td>
        </tr>
        <tr>
            <td><span class="code">+</span></td>
            <td><span class="desc">重复一次或更多次</span></td>
        </tr>
        <tr>
            <td><span class="code">?</span></td>
            <td><span class="desc">重复零次或一次</span></td>
        </tr>
        <tr>
            <td><span class="code">{n}</span></td>
            <td><span class="desc">重复n次</span></td>
        </tr>
        <tr>
            <td><span class="code">{n,}</span></td>
            <td><span class="desc">重复n次或更多次</span></td>
        </tr>
        <tr>
            <td><span class="code">{n,m}</span></td>
            <td><span class="desc">重复n到m次</span></td>
        </tr>
    </tbody>
</table>
<p>下面是一些使用重复的例子：</p>
<p><span class="regex">Windows\d+</span>匹配<span class="desc">Windows后面跟1个或更多数字</span></p>
<p><span class="regex">13\d{9}</span>匹配<span class="desc">13后面跟9个数字(中国的手机号)</span></p>
<p><span class="regex">^\w+</span>匹配<span class="desc">一行的第一个单词(或整个字符串的第一个单词，具体匹配哪个意思得看选项设置)</span></p>
<h2 id="charclass">字符类</h2>
<p>要想查找数字，字母或数字，空白是很简单的，因为已经有了对应这些字符集合的元字符，但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办？</p>
<p>很简单，你只需要在中括号里列出它们就行了，像<span class="regex">[aeiou]</span>就匹配<span class="desc">任何一个英文元音字母</span>，<span class="regex">[.?!]</span>匹配<span class="desc">标点符号(.或?或!)</span>(英文语句通常只以这三个标点结束)。</p>
<p>我们也可以轻松地指定一个字符<span class="name">范围</span>，像<span class="regex">[0-9]</span>代表的含意与<span class="regex">\d</span>就是完全一致的：<span class="desc">一位数字</span>，同理<span class="regex">[a-z0-9A-Z_]</span>也完全等同于<span class="code">\w</span>（如果只考虑英文的话）。</p>
<p>下面是一个更复杂的表达式：<span class="regex">\(?0\d{2}[) -]?\d{8}</span>。</p>
<p>这个表达式可以匹配<span class="desc">几种格式的电话号码</span>，像<span class="string">(010)88886666</span>，或<span class="string">022-22334455</span>，或<span class="string">02912345678</span>等。我们对它进行一些分析吧：首先是一个转义字符<span class="part">\(</span>,它能出现0次或1次(<span class="part">?</span>),然后是一个<span class="part">0</span>，后面跟着2个数字(<span class="part">\d{2}</span>)，然后是<span class="part">)</span>或<span class="part">-</span>或<span class="part">空格</span>中的一个，它出现1次或不出现(<span class="part">?</span>)，最后是8个数字(<span class="part">\d{8}</span>)。不幸的是，它也能匹配<span class="string">010)12345678</span>或<span class="string">(022-87654321</span>这样的&#8220;不正确&#8221;的格式。要解决这个问题，请在本教程的下面查找答案。</p>
<h2 id="negation">反义</h2>
<p>有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外，其它任意字符都行的情况，这时需要用到<span class="name">反义</span>：</p>
<table cellspacing="0">
    <caption>表3.常用的反义代码</caption>
    <tbody>
        <tr>
            <th>代码/语法</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><span class="code">\W</span></td>
            <td><span class="desc">匹配任意不是字母，数字，下划线，汉字的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\S</span></td>
            <td><span class="desc">匹配任意不是空白符的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\D</span></td>
            <td><span class="desc">匹配任意非数字的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\B</span></td>
            <td><span class="desc">匹配不是单词开头或结束的位置</span></td>
        </tr>
        <tr>
            <td><span class="code">[^x]</span></td>
            <td><span class="desc">匹配除了x以外的任意字符</span></td>
        </tr>
        <tr>
            <td><span class="code">[^aeiou]</span></td>
            <td><span class="desc">匹配除了aeiou这几个字母以外的任意字符</span></td>
        </tr>
    </tbody>
</table>
<p>例子：<span class="regex">\S+</span>匹配<span class="desc">不包含空白符的字符串</span>。</p>
<p><span class="regex">&lt;a[^&gt;]+&gt;</span>匹配<span class="desc">用尖括号括起来的以a开头的字符串</span>。</p>
<h2 id="alternative">替换</h2>
<p>好了，现在终于到了解决3位或4位区号问题的时间了。正则表达式里的<span class="name">替换</span>指的是有几种规则，如果满足其中任意一种规则都应该当成匹配，具体方法是用<span class="code">|</span>把不同的规则分隔开。听不明白？没关系，看例子：</p>
<p><span class="regex">0\d{2}-\d{8}|0\d{3}-\d{7}</span>这个表达式能<span class="desc">匹配两种以连字号分隔的电话号码：一种是三位区号，8位本地号(如010-12345678)，一种是4位区号，7位本地号(0376-2233445)</span>。</p>
<p><span class="regex">\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}</span>这个表达式<span class="desc">匹配3位区号的电话号码，其中区号可以用小括号括起来，也可以不用，区号与本地号间可以用连字号或空格间隔，也可以没有间隔</span>。你可以试试用替换|把这个表达式扩展成也支持4位区号的。</p>
<p><span class="regex">\d{5}-\d{4}|\d{5}</span>这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字，或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题：<strong>使用替换时，顺序是很重要的</strong>。如果你把它改成<span class="regex">\d{5}|\d{5}-\d{4}</span>的话，那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配替换时，将会从左到右地测试每个分枝条件，如果满足了某个分枝的话，就不会去管其它的替换条件了。</p>
<p><span class="regex">Windows98|Windows2000|WindosXP</span>这个例子是为了告诉你替换不仅仅能用于两种规则，也能用于更多种规则。</p>
<h2 id="grouping">分组</h2>
<p>我们已经提到了怎么重复单个字符（直接在字符后面加上限定符就行了）；但如果想要重复多个字符又该怎么办？你可以用小括号来指定<span class="name">子表达式</span>(也叫做<span class="name">分组</span>)，然后你就可以指定这个子表达式的重复次数了，你也可以对子表达式进行其它一些操作(后面会有介绍)。</p>
<p><span class="regex">(\d{1,3}\.){3}\d{1,3}</span>是一个<span class="desc">简单的IP地址匹配</span>表达式。要理解这个表达式，请按下列顺序分析它：<span class="part">\d{1,3}</span>匹配<span class="desc">1到3位的数字</span>，<span class="part">(\d{1,3}\.){3}</span>匹配<span class="desc">三位数字加上一个英文句号(这个整体也就是这个<span class="name">分组</span>)重复3次</span>，最后再加上<span class="desc">一个一到三位的数字</span>(<span class="part">\d{1,3}</span>)。</p>
<p>不幸的是，它也将匹配<span class="string">256.300.888.999</span>这种不可能存在的IP地址(IP地址中每个数字都不能大于255。题外话，好像反恐24小时第三季的编剧不知道这一点，汗...)。如果能使用算术比较的话，或许能简单地解决这个问题，但是正则表达式中并不提供关于数学的任何功能，所以只能使用冗长的分组，选择，字符类来描述一个正确的IP地址：<span class="regex">((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)</span>。</p>
<p>理解这个表达式的关键是理解<span class="part">2[0-4]\d|25[0-5]|[01]?\d\d?</span>，这里我就不细说了，你自己应该能分析得出来它的意义。</p>
<h2 id="backreference">后向引用</h2>
<p>使用小括号指定一个子表达式后，<strong>匹配这个子表达式的文本</strong>(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下，每个分组会自动拥有一个<span class="name">组号</span>，规则是：从左向右，以分组的左括号为标志，第一个出现的分组的组号为1，第二个为2，以此类推。</p>
<p><span class="name">后向引用</span>用于重复搜索前面某个分组匹配的文本。例如，<span class="part">\1</span>代表<span class="desc">分组1匹配的文本</span>。难以理解？请看示例：</p>
<p><span class="regex">\b(\w+)\b\s+\1\b</span>可以用来匹配<span class="desc">重复的单词</span>，像<span class="string">go go</span>, <span class="string">kitty kitty</span>。首先是<span class="desc">一个单词</span>，也就是<span class="desc">单词开始处和结束处之间的多于一个的字母或数字</span>(<span class="part">\b(\w+)\b</span>)，然后是<span class="desc">1个或几个空白符</span>(<span class="part">\s+</span>)，最后是<span class="desc">前面匹配的那个单词</span>(<span class="part">\1</span>)。</p>
<p>你也可以自己指定子表达式的<span class="name">组名</span>。要指定一个子表达式的组名，请使用这样的语法：<span class="code">(?&lt;Word&gt;\w+)</span>(或者把尖括号换成<span class="code">'</span>也行：<span class="code">(?'Word'\w+)</span>),这样就把<span class="part">\w+</span>的组名指定为<span class="part">Word</span>了。要反向引用这个分组<span class="name">捕获</span>的内容，你可以使用<span class="code">\k&lt;Word&gt;</span>,所以上一个例子也可以写成这样：<span class="regex">\b(?&lt;Word&gt;\w+)\b\s+\k&lt;Word&gt;\b</span>。</p>
<p>使用小括号的时候，还有很多特定用途的语法。下面列出了最常用的一些：</p>
<table cellspacing="0">
    <caption>表4.分组语法</caption>
    <tbody>
        <tr>
            <th colspan="2">捕获</th>
        </tr>
        <tr>
            <td><span class="code">(exp)</span></td>
            <td><span class="desc">匹配exp,并捕获文本到自动命名的组里</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&lt;name&gt;exp)</span></td>
            <td><span class="desc">匹配exp,并捕获文本到名称为name的组里，也可以写成(?'name'exp)</span></td>
        </tr>
        <tr>
            <td><span class="code">(?:exp)</span></td>
            <td><span class="desc">匹配exp,不捕获匹配的文本，也不给此分组分配组号</span></td>
        </tr>
        <tr>
            <th colspan="2">零宽断言</th>
        </tr>
        <tr>
            <td><span class="code">(?=exp)</span></td>
            <td><span class="desc">匹配exp前面的位置</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&lt;=exp)</span></td>
            <td><span class="desc">匹配exp后面的位置</span></td>
        </tr>
        <tr>
            <td><span class="code">(?!exp)</span></td>
            <td><span class="desc">匹配后面跟的不是exp的位置</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&lt;!exp)</span></td>
            <td><span class="desc">匹配前面不是exp的位置</span></td>
        </tr>
        <tr>
            <th colspan="2">注释</th>
        </tr>
        <tr>
            <td><span class="code">(?#comment)</span></td>
            <td><span class="desc">这种类型的组不对正则表达式的处理产生任何影响，用于提供注释让人阅读</span></td>
        </tr>
    </tbody>
</table>
<p>我们已经讨论了前两种语法。第三个<span class="code">(?:exp)</span>不会改变正则表达式的处理方式，只是这样的组匹配的内容<span class="desc">不会像前两种那样被捕获到某个组里面</span>。</p>
<h2 id="lookaround">零宽断言</h2>
<p>接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西，也就是说它们像<span class="code">\b</span>,<span class="code">^</span>,<span class="code">$</span>那样用于指定一个位置，这个位置应该满足一定的条件(<a href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#reference">断言</a>)，因此它们也被称为<span class="name">零宽断言</span>。最好还是拿例子来说明吧：</p>
<p><span class="code">(?=exp)</span>也叫<span class="name">零宽度正预测先行断言</span>，它<span class="desc">断言自身出现的位置的后面能匹配表达式exp</span>。比如<span class="regex">\b\w+(?=ing\b)</span>，匹配<span class="desc">以ing结尾的单词的前面部分(除了ing以外的部分)</span>，如查找<span class="string">I'm singing while you're dancing.</span>时，它会匹配<span class="desc">sing</span>和<span class="desc">danc</span>。</p>
<p><span class="code">(?&lt;=exp)</span>也叫<span class="name">零宽度正回顾后发断言</span>，它<span class="desc">断言自身出现的位置的前面能匹配表达式exp</span>。比如<span class="regex">(?&lt;=\bre)\w+\b</span>会匹配<span class="desc">以re开头的单词的后半部分(除了re以外的部分)</span>，例如在查找<span class="string">reading a book</span>时，它匹配<span class="desc">ading</span>。</p>
<p>假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了)，你可以这样查找需要在前面和里面添加逗号的部分：<span class="regex">((?&lt;=\d)\d{3})*\b</span>，用它对<span class="string">1234567890</span>进行查找时结果是<span class="desc">234567890</span>。</p>
<p>下面这个例子同时使用了这两种断言：<span class="regex">(?&lt;=\s)\d+(?=\s)</span>匹配<span class="desc">以空白符间隔的数字(再次强调，不包括这些空白符)</span>。</p>
<h2 id="negativelookaround">负向零宽断言</h2>
<p>前面我们提到过怎么查找<strong>不是某个字符或不在某个字符类里</strong>的字符的方法(反义)。但是如果我们只是想要<strong>确保某个字符没有出现，但并不想去匹配它</strong>时怎么办？例如，如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样：</p>
<p><span class="regex">\b\w*q[^u]\w*\b</span>匹配<span class="desc">包含<strong>后面不是字母u的字母q</strong>的单词</span>。但是如果多做测试(或者你思维足够敏锐，直接就观察出来了)，你会发现，如果q出现在单词的结尾的话，像<strong>Iraq</strong>,<strong>Benq</strong>，这个表达式就会出错。这是因为<span class="part">[^u]</span>总要匹配一个字符，所以如果q是单词的最后一个字符的话，后面的<span class="part">[^u]</span>将会匹配q后面的单词分隔符(可能是空格，或者是句号或其它的什么)，后面的<span class="part">\w*\b</span>将会匹配下一个单词，于是<span class="regex">\b\w*q[^u]\w*\b</span>就能匹配整个<span class="string">Iraq fighting</span>。<span class="name">负向零宽断言</span>能解决这样的问题，因为它只匹配一个位置，并不<strong>消费</strong>任何字符。现在，我们可以这样来解决这个问题：<span class="regex">\b\w*q(?!u)\w*\b</span>。</p>
<p><span class="name">零宽度负预测先行断言</span><span class="code">(?!exp)</span>，<span class="desc">断言此位置的后面不能匹配表达式exp</span>。例如：<span class="regex">\d{3}(?!\d)</span>匹配<span class="desc">三位数字，而且这三位数字的后面不能是数字</span>；<span class="regex">\b((?!abc)\w)+\b</span>匹配<span class="desc">不包含连续字符串abc的单词</span>。</p>
<p>同理，我们可以用<span class="code">(?&lt;!exp)</span>,<span class="name">零宽度正回顾后发断言</span>来<span class="desc">断言此位置的前面不能匹配表达式exp</span>：<span class="regex">(?&lt;![a-z])\d{7}</span>匹配<span class="desc">前面不是小写字母的七位数字</span>。</p>
<p>一个更复杂的例子：<span class="regex">(?&lt;=&lt;(\w+)&gt;).*(?=&lt;\/\1&gt;)</span>匹配<span class="desc">不包含属性的简单HTML标签内里的内容</span>。<span class="code">(&lt;?(\w+)&gt;)</span>指定了这样的<span class="name">前缀</span>：<span class="desc">被尖括号括起来的单词</span>(比如可能是&lt;b&gt;)，然后是<span class="part">.*</span>(任意的字符串),最后是一个<span class="name">后缀</span><span class="part">(?=&lt;\/\1&gt;)</span>。注意后缀里的<span class="part">\/</span>，它用到了前面提过的字符转义；<span class="part">\1</span>则是一个反向引用，引用的正是<span class="desc">捕获的第一组</span>，前面的<span class="part">(\w+)</span>匹配的内容，这样如果前缀实际上是&lt;b&gt;的话，后缀就是&lt;/b&gt;了。整个表达式匹配的是&lt;b&gt;和&lt;/b&gt;之间的内容(再次提醒，不包括前缀和后缀本身)。</p>
<h2 id="commenting">注释</h2>
<p>小括号的另一种用途是能过语法<span class="code">(?#comment)</span>来包含注释。例如：<span class="regex">2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)</span>。</p>
<p>要包含注释的话，最好是启用&#8220;忽略模式里的空白符&#8221;选项，这样在编写表达式时能任意的添加空格，Tab，换行，而实际使用时这些都将被忽略。启用这个选项后，在#后面到这一行结束的所有文本都将被当成注释忽略掉。</p>
<p>例如，我们可以前面的一个表达式写成这样：</p>
<pre class="regex">      (?&lt;=    # 断言要匹配的文本的前缀
&lt;(\w+)&gt; # 查找尖括号括起来的字母或数字(即HTML/XML标签)
)       # 前缀结束
.*      # 匹配任意文本
(?=     # 断言要匹配的文本的后缀
&lt;\/\1&gt;  # 查找尖括号括起来的内容：前面是一个"/"，后面是先前捕获的标签
)       # 后缀结束
</pre>
<h2 id="greedyandlazy">贪婪与懒惰</h2>
<p>当正则表达式中包含能接受重复的限定符时，通常的行为是（在使整个表达式能得到匹配的前提下）匹配<strong>尽可能多</strong>的字符。考虑这个表达式：<span class="regex">a.*b</span>，它将会匹配<span class="desc">最长的以a开始，以b结束的字符串</span>。如果用它来搜索<span class="string">aabab</span>的话，它会匹配整个字符串<span class="desc">aabab</span>。这被称为<span class="name">贪婪</span>匹配。</p>
<p>有时，我们更需要<span class="name">懒惰</span>匹配，也就是匹配<strong>尽可能少</strong>的字符。前面给出的限定符都可以被转化为懒惰匹配模式，只要在它后面加上一个问号<span class="code">?</span>。这样<span class="regex">.*?</span>就意味着<span class="desc">匹配任意数量的重复，但是在能使整个匹配成功的前提下使用最少的重复</span>。现在看看懒惰版的例子吧：</p>
<p><span class="regex">a.*?b</span>匹配<span class="desc">最短的，以a开始，以b结束的字符串</span>。如果把它应用于<span class="string">aabab</span>的话，它会匹配<span class="desc">aab</span>和<span class="desc">ab</span>（为什么第一个匹配是aab而不是ab？简单地说，因为正则表达式有另一条规则，比懒惰／贪婪规则的优先级更高：最先开始的匹配拥有最高的优先权——The Match That Begins Earliest Wins）。</p>
<table cellspacing="0">
    <caption>表5.懒惰限定符</caption>
    <tbody>
        <tr>
            <td><span class="code">*?</span></td>
            <td><span class="desc">重复任意次，但尽可能少重复</span></td>
        </tr>
        <tr>
            <td><span class="code">+?</span></td>
            <td><span class="desc">重复1次或更多次，但尽可能少重复</span></td>
        </tr>
        <tr>
            <td><span class="code">??</span></td>
            <td><span class="desc">重复0次或1次，但尽可能少重复</span></td>
        </tr>
        <tr>
            <td><span class="code">{n,m}?</span></td>
            <td><span class="desc">重复n到m次，但尽可能少重复</span></td>
        </tr>
        <tr>
            <td><span class="code">{n,}?</span></td>
            <td><span class="desc">重复n次以上，但尽可能少重复</span></td>
        </tr>
    </tbody>
</table>
<h2 id="regexoptions">处理选项</h2>
<p>上面介绍了几个选项如忽略大小写，处理多行等，这些选项能用来改变处理正则表达式的方式。下面是.Net中常用的正则表达式选项：</p>
<table cellspacing="0">
    <caption>表6.常用的处理选项</caption>
    <thead>
        <tr>
            <th>名称</th>
            <th>说明</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>IgnoreCase(忽略大小写)</td>
            <td>匹配时不区分大小写。</td>
        </tr>
        <tr>
            <td>Multiline(多行模式)</td>
            <td>更改<span class="code">^</span>和<span class="code">$</span>的含义，使它们分别在任意一行的行首和行尾匹配，而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,<span class="code">$</span>的精确含意是:匹配\n之前的位置以及字符串结束前的位置.) </td>
        </tr>
        <tr>
            <td>Singleline(单行模式)</td>
            <td>更改<span class="code">.</span>的含义，使它与每一个字符匹配（包括换行符\n）。 </td>
        </tr>
        <tr>
            <td>IgnorePatternWhitespace(忽略空白)</td>
            <td>忽略表达式中的非转义空白并启用由<span class="code">#</span>标记的注释。</td>
        </tr>
        <tr>
            <td>RightToLeft(从右向左查找)</td>
            <td>匹配从右向左而不是从左向右进行。</td>
        </tr>
        <tr>
            <td>ExplicitCapture(显式捕获)</td>
            <td>仅捕获已被显式命名的组。</td>
        </tr>
        <tr>
            <td>ECMAScript(JavaScript兼容模式)</td>
            <td>使表达式的行为与它在JavaScript里的行为一致。</td>
        </tr>
    </tbody>
</table>
<p>一个经常被问到的问题是：是不是只能同时使用多行模式和单行模式中的一种？答案是：不是。这两个选项之间没有任何关系，除了它们的名字比较相似（以至于让人感到疑惑）以外。</p>
<h2 id="balancedgroup">平衡组/递归匹配</h2>
<p class="important">注意：这里介绍的平衡组语法是由.Net Framework支持的；其它语言／库不一定支持这种功能，或者支持此功能但需要使用不同的语法。 </p>
<p>有时我们需要匹配像<span class="desc">( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构</span>，这时简单地使用<span class="code">\(.+\)</span>则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式，懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等，比如<span class="string">( 5 / ( 3 + 2 ) ) )</span>，那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的，配对的括号之间的内容呢？ </p>
<p>为了避免<span class="code">(</span>和<span class="code">\(</span>把你的大脑彻底搞糊涂，我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把<span class="string">xx &lt;aa &lt;bbb&gt; &lt;bbb&gt; aa&gt; yy</span>这样的字符串里，最长的配对的尖括号内的内容捕获出来？ </p>
<p>这里需要用到以下的语法构造：</p>
<ul>
    <li><span class="code">(?'group')</span> 把捕获的内容命名为group,并压入堆栈
    <li><span class="code">(?'-group')</span> 从堆栈上弹出最后压入堆栈的名为group的捕获内容，如果堆栈本来为空，则本分组的匹配失败
    <li><span class="code">(?(group)yes|no)</span> 如果堆栈上存在以名为group的捕获内容的话，继续匹配yes部分的表达式，否则继续匹配no部分
    <li><span class="code">(?!)</span> 零宽负向先行断言，由于没有后缀表达式，试图匹配总是失败 </li>
</ul>
<p>如果你不是一个程序员（或者你是一个对堆栈的概念不熟的程序员），你就这样理解上面的三种语法吧：第一个就是在黑板上写一个 "group"，第二个就是从黑板上擦掉一个"group"，第三个就是看黑板上写的还有没有"group"，如果有就继续匹配yes部分，否则就匹配 no部分。</p>
<p>我们需要做的是每碰到了左括号，就在黑板上写一个"group"，每碰到一个右括号，就擦掉一个，到了最后就看看黑板上还有没有－－如果有那就证明左括号比右括号多，那匹配就应该失败。 </p>
<pre class="regex">&lt;                         #最外层的左括号
[^&lt;&gt;]*                #最外层的左括号后面的不是括号的内容
(
(
(?'Open'&lt;)    #碰到了左括号，在黑板上写一个"Open"
[^&lt;&gt;]*       #匹配左括号后面的不是括号的内容
)+
(
(?'-Open'&gt;)   #碰到了右括号，擦掉一个"Open"
[^&lt;&gt;]*        #匹配右括号后面不是括号的内容
)+
)*
(?(Open)(?!))         #在遇到最外层的右括号前面，判断黑板上还有没有没擦掉的"Open"；如果还有，则匹配失败
&gt;                         #最外层的右括号</pre>
<p>平衡组的一个最常见的应用就是匹配HTML,下面这个例子可以匹配<span class="desc">嵌套的&lt;div&gt;标签</span>：<span class="regex">&lt;div[^&gt;]*&gt;[^&lt;&gt;]*(((?'Open'&lt;div[^&gt;]*&gt;)[^&lt;&gt;]*)+((?'-Open'&lt;/div&gt;)[^&lt;&gt;]*)+)*(?(Open)(?!))&lt;/div&gt;</span>.</p>
<h2 id="more">还有些什么东西没提到</h2>
<p>我已经描述了构造正则表达式的大量元素，还有一些我没有提到的东西。下面是未提到的元素的列表，包含语法和简单的说明。你可以在网上找到更详细的参考资料来学习它们--当你需要用到它们的时候。如果你安装了MSDN Library,你也可以在里面找到关于.net下正则表达式详细的文档。</p>
<table cellspacing="0">
    <caption>表7.尚未详细讨论的语法</caption>
    <tbody>
        <tr>
            <td><span class="code">\a</span></td>
            <td><span class="desc">报警字符(打印它的效果是电脑嘀一声)</span></td>
        </tr>
        <tr>
            <td><span class="code">\b</span></td>
            <td><span class="desc">通常是单词分界位置，但如果在字符类里使用代表退格</span></td>
        </tr>
        <tr>
            <td><span class="code">\t</span></td>
            <td><span class="desc">制表符，Tab</span></td>
        </tr>
        <tr>
            <td><span class="code">\r</span></td>
            <td><span class="desc">回车</span></td>
        </tr>
        <tr>
            <td><span class="code">\v</span></td>
            <td><span class="desc">竖向制表符</span></td>
        </tr>
        <tr>
            <td><span class="code">\f</span></td>
            <td><span class="desc">换页符</span></td>
        </tr>
        <tr>
            <td><span class="code">\n</span></td>
            <td><span class="desc">换行符</span></td>
        </tr>
        <tr>
            <td><span class="code">\e</span></td>
            <td><span class="desc">Escape</span></td>
        </tr>
        <tr>
            <td><span class="code">\0nn</span></td>
            <td><span class="desc">ASCII代码中八进制代码为nn的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\xnn</span></td>
            <td><span class="desc">ASCII代码中十六进制代码为nn的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\unnnn</span></td>
            <td><span class="desc">Unicode代码中十六进制代码为nnnn的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\cN</span></td>
            <td><span class="desc">ASCII控制字符。比如\cC代表Ctrl+C</span></td>
        </tr>
        <tr>
            <td><span class="code">\A</span></td>
            <td><span class="desc">字符串开头(类似^，但不受处理多行选项的影响)</span></td>
        </tr>
        <tr>
            <td><span class="code">\Z</span></td>
            <td><span class="desc">字符串结尾或行尾(不受处理多行选项的影响)</span></td>
        </tr>
        <tr>
            <td><span class="code">\z</span></td>
            <td><span class="desc">字符串结尾(类似$，但不受处理多行选项的影响)</span></td>
        </tr>
        <tr>
            <td><span class="code">\G</span></td>
            <td><span class="desc">当前搜索的开头</span></td>
        </tr>
        <tr>
            <td><span class="code">\p{name}</span></td>
            <td><span class="desc">Unicode中命名为name的字符类，例如\p{IsGreek}</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&gt;exp)</span></td>
            <td><span class="desc">贪婪子表达式</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&lt;x&gt;-&lt;y&gt;exp)</span></td>
            <td><span class="desc">平衡组</span></td>
        </tr>
        <tr>
            <td><span class="code">(?im-nsx:exp)</span></td>
            <td><span class="desc">在子表达式exp中改变处理选项</span></td>
        </tr>
        <tr>
            <td><span class="code">(?im-nsx)</span></td>
            <td><span class="desc">为表达式后面的部分改变处理选项</span></td>
        </tr>
        <tr>
            <td><span class="code">(?(exp)yes|no)</span></td>
            <td><span class="desc">把exp当作零宽正向先行断言，如果在这个位置能匹配，使用yes作为此组的表达式；否则使用no</span></td>
        </tr>
        <tr>
            <td><span class="code">(?(exp)yes)</span></td>
            <td><span class="desc">同上，只是使用空表达式作为no</span></td>
        </tr>
        <tr>
            <td><span class="code">(?(name)yes|no)</span></td>
            <td><span class="desc">如果命名为name的组捕获到了内容，使用yes作为表达式；否则使用no</span></td>
        </tr>
        <tr>
            <td><span class="code">(?(name)yes)</span></td>
            <td><span class="desc">同上，只是使用空表达式作为no</span></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/liuzheng/aggbug/180658.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-02-19 14:04 <a href="http://www.blogjava.net/liuzheng/articles/180658.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Jakarta Commons  HttpClient 学习笔记（转）</title><link>http://www.blogjava.net/liuzheng/articles/180506.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Mon, 18 Feb 2008 09:49:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/180506.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/180506.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/180506.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/180506.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/180506.html</trackback:ping><description><![CDATA[<h4>1、HttpClient的功能</h4>
<br />
<ol><br />
    <li>基于标准，纯正<a href="http://www.myfaq.com.cn/Dev/Programme/Java/index.html" target="_blank">java</a>，实现了http1.0和1.1。<br />
    <li>在一个可扩展的OO框架内，实现了HTTP的全部方法(GET, POST, <br />
    PUT, DELETE, HEAD, OPTIONS, and TRACE)<br />
    <li>支持HTTPS(ssl上的HTTP)的加密操作<br />
    <li>透明地穿过HTTP代理建立连接<br />
    <li>通过CONNECT方法，利用通过建立穿过HTTP代理的HTTPS连接<br />
    <li>利用本地Java socket，透明地穿过SOCKS(版本5和4）代理建立连接<br />
    <li>支持利用Basic、Digest和NTLM加密的<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a><br />
    <li>支持用于上传大<a href="http://www.myfaq.com.cn/Soft/Tools/File/index.html" target="_blank">文件</a>的Multi-Part表单POST方法<br />
    <li>插件式安全socket实现，易于使用第三方的解决方案<br />
    <li>连接管理，支持多线程应用，支持设定单个主机总连接和最高连接数量,自动检测和关闭失效连接<br />
    <li>直接将请求信息流送到<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>的端口<br />
    <li>直接读取从<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>的端口送出的应答信息<br />
    <li>支持HTTP/1.0中用KeepAlive和HTTP/1.1中用persistance设置的持久连接<br />
    <li>直接访问由<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>送出的应答代码和头部信息<br />
    <li>可设置连接超时时间
    <li><br />
    <li>HttpMethods 实现Command Pattern，以允许并行请求或高效连接复用<br />
    <li>遵循the Apache Software License协议，源码免费可得 <br />
    </li>
</ol>
<br />
<h4>2、预备工作</h4>
<br />
　　对jre1.3.*，如果要HttpClient支持https，则需要下载并安装<a href="http://<a%20href=/" target="_blank" http: www.myfaq.com.cn Dev Programme Java index.html?>java</a>.sun.com/products/jsse/"&gt;jsse</a>和<a href="http://<a%20href=/" target="_blank" http: www.myfaq.com.cn Dev Programme Java index.html?>java</a>.sun.com/products/jce/"&gt;jce</a>.安装的步骤如下：<br />
1)下载jsse和jce.<br />
2)检查CLASSPATH中没有与jsse和jce相关的jar包<br />
3)将 US_export_policy.jar、local_policy.jar、jsse.jar、jnet.jar、jce1_2_x.jar、sunjce_provider.jar、jcert.jar复制到目录：<br />
UNIX:$JDK_HOME/jre/lib/ext<br />
Windows:%JDK_HOME%\jre\lib\ext<br />
4)修改下述目录下的<a href="http://www.myfaq.com.cn/Dev/Programme/Java/index.html" target="_blank">java</a>.security<a href="http://www.myfaq.com.cn/Soft/Tools/File/index.html" target="_blank">文件</a>。<br />
UNIX:$JDK_HOME/jre/lib/security/<br />
Windows:%JDK_HOME%\jre\lib\security\<br />
5)
<div class="code">将<br />
#<br />
# List of providers and their preference orders:<br />
#<br />
security.provider.1=sun.security.provider.Sun<br />
security.provider.2=com.sun.rsajca.Provider<br />
改为：<br />
#<br />
# List of providers and their preference orders:<br />
#<br />
security.provider.1=com.sun.crypto.provider.SunJCE<br />
security.provider.2=sun.security.provider.Sun<br />
security.provider.3=com.sun.rsajca.Provider<br />
security.provider.4=com.sun.net.ssl.internal.ssl.Provider</div>
<br />
　　HttpClient还要求安装commons-logging，下面跟httpclient一块安装。<br />
<h4>3、取得源码</h4>
<br />
<div class="code">cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login<br />
password: anoncvs<br />
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/logging<br />
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/httpclient</div>
<br />
　　编译：
<div class="code">cd jakarta-commons/logging<br />
ant dist<br />
cp dis/*.jar ../httpclient/lib/<br />
cd ../httpclient<br />
ant dist</div>
<br />
<h4>4、使用HttpClient编程的基本步聚</h4>
<br />
<ol>
    <li>创建 HttpClient 的一个实例.<br />
    <li>创建某个方法（DeleteMethod，EntityEnclosingMethod，ExpectContinueMethod，GetMethod，HeadMethod，MultipartPostMethod，OptionsMethod，PostMethod，PutMethod，TraceMethod）的一个实例，一般可用要目标URL为参数。<br />
    <li>让 HttpClient 执行这个方法.<br />
    <li>读取应答信息.<br />
    <li>释放连接.<br />
    <li>处理应答.<br />
    </li>
</ol>
<br />
　　在执行方法的过程中，有两种异常，一种是HttpRecoverableException，表示偶然性错误发生，一般再试可能成功，另一种是IOException，严重错误。<br />
　　这儿有这个教程中的一个例程，可以<a href="http://hedong.3322.org/archives/docs/HttpClientTutorial.<a%20href=" target="_blank" http: www.myfaq.com.cn Dev Programme Java index.html?>java</a>"&gt;下载</a>。
<h4>5、<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a></h4>
<br />
　　HttpClient三种不同的<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>方案: Basic, Digest and NTLM. 这些方案可用于<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>或代理对客户端的<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>，简称<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a><a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>或代理<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>。<br />
1)<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a><a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>(Server Authentication)<br />
　　HttpClient处理<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a><a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>几乎是透明的，仅需要开发人员提供登录信息(login credentials)。登录信息保存在HttpState类的实例中，可以通过 setCredentials(String realm, Credentials cred)和getCredentials(String realm)来获取或设置。注意，设定对非特定站点访问所需要的登录信息，将realm参数置为null. HttpClient内建的自动<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>，可以通过HttpMethod类的setDoAuthentication(boolean doAuthentication)方法关闭，而且这次关闭只影响HttpMethod当前的实例。<br />
　　抢先<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>(Preemptive Authentication)可以通过下述方法打开.
<div class="code">client.getState().setAuthenticationPreemptive(true);</div>
　　在这种模式时，HttpClient会主动将basic<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>应答信息传给<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>，即使在某种情况下<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>可能返回<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>失败的应答，这样做主要是为了减少连接的建立。为使每个新建的 HttpState实例都实行抢先<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>，可以如下设置<a href="http://www.myfaq.com.cn/System/index.html" target="_blank">系统</a>属性。
<div class="code">setSystemProperty(Authenticator.PREEMPTIVE_PROPERTY, "true");</div>
<br />
Httpclient实现的抢先<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>遵循rfc2617.<br />
2)代理<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>(proxy authentication)<br />
　　除了登录信息需单独存放以外，代理<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>与<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a><a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>几乎一致。用 setProxyCredentials(String realm, Credentials cred)和 getProxyCredentials(String realm)设、取登录信息。<br />
3)<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>方案(authentication schemes)<br />
　　Basic<br />
　　是HTTP中规定最早的也是最兼容(?)的方案，遗憾的是也是最不安全的一个方案，因为它以明码传送用户名和密码。它要求一个UsernamePasswordCredentials实例，可以指定<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>端的访问空间或采用默认的登录信息。<br />
　　Digest<br />
　　是在HTTP1.1中增加的一个方案，虽然不如Basic得到的软件支持多，但还是有广泛的使用。Digest方案比Basic方案安全得多，因它根本就不通过<a href="http://www.myfaq.com.cn/Net/index.html" target="_blank">网络</a>传送实际的密码，传送的是利用这个密码对从<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>传来的一个随机数(nonce)的加密串。它要求一个UsernamePasswordCredentials实例，可以指定<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>端的访问空间或采用默认的登录信息。<br />
　　NTLM<br />
　　这是HttpClient支持的最复杂的<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>协议。它M$设计的一个私有协议，没有公开的规范说明。一开始由于设计的缺陷，NTLM的安全性比Digest差，后来经过一个ServicePack补丁后，安全性则比较Digest高。NTLM需要一个NTCredentials实例. 注意，由于NTLM不使用访问空间(realms)的概念，HttpClient利用<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>的域名作访问空间的名字。还需要注意，提供给NTCredentials的用户名，不要用域名的前缀 - 如: "adrian" 是正确的，而 "DOMAIN\adrian" 则是错的.<br />
　　NTLM<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>的工作机制与basic和digest有很大的差别。这些差别一般由HttpClient处理，但理解这些差别有助避免在使用NTLM<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>时出现错误。<br />
<ol>
    <li>从HttpClientAPI的角度来看，NTLM与其它<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>方式一样的工作，差别是需要提供'NTCredentials'实例而不是'UsernamePasswordCredentials'(其实，前者只是扩展了后者)<br />
    <li>对NTLM<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>，访问空间是连接到的机器的域名，这对多域名主机会有一些麻烦.只有HttpClient连接中指定的域名才是<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>用的域名。建议将realm设为null以使用默认的设置。<br />
    <li>NTLM只是<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>了一个连接而不是一请求，所以每当一个新的连接建立就要进行一次<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>，且在<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>的过程中保持连接是非常重要的。 因此，NTLM不能同时用于代理<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>和<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a><a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>，也不能用于http1.0连接或<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>不支持持久连接的情况。</li>
</ol>
<h4>6、重定向</h4>
<br />
　　由于技术限制，以及为保证2.0发布版API的稳定，HttpClient还不能自动处重定向，但对重定向到同一主机、同一端口且采用同一协议的情况HttpClient可以支持。不能自动的处理的情况，包括需要人工交互的情况，或超出httpclient的能力。<br />
　　当<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>重定向指令指到不同的主机时，HttpClient只是简单地将重定向状态码作为应答状态。所有的300到399（包含两端）的返回码，都表示是重定向应答。常见的有：<br />
<ol>
    <li>301 永久移动. HttpStatus.SC_MOVED_PERMANENTLY<br />
    <li>302 临时移动. HttpStatus.SC_MOVED_TEMPORARILY<br />
    <li>303 See Other. HttpStatus.SC_SEE_OTHER<br />
    <li>307 临时重定向. HttpStatus.SC_TEMPORARY_REDIRECT</li>
</ol>
<br />
　　当收到简单的重定向时，<a href="http://www.myfaq.com.cn/Dev/index.html" target="_blank">程序</a>应从HttpMethod对象中抽取新的URL并将其下载。另外,限制一下重定向次数是个好的主意，这可以避免递归循环。新的URL可以从头字段Location中抽取，如下：
<div class="code">String redirectLocation;<br />
Header locationHeader = method.getResponseHeader("location");<br />
if (locationHeader != null) {<br />
redirectLocation = locationHeader.getValue();<br />
} else {<br />
// The response is invalid and did not provide the new location for<br />
// the resource. Report an error or possibly handle the response<br />
// like a 404 Not Found error.<br />
}</div>
<br />
特殊重定向：<br />
<ol>
    <li>300 多重选择. HttpStatus.SC_MULTIPLE_CHOICES<br />
    <li>304 没有改动. HttpStatus.SC_NO T_MODIFIED<br />
    <li>305 使用代理. HttpStatus.SC_USE_PROXY <br />
    </li>
</ol>
　　
<h4>7、<a href="http://www.myfaq.com.cn/Dev/Programme/VC/Str/index.html" target="_blank">字符</a>编码(character encoding)</h4>
<br />
　　一个HTTP协议的请求或应答的头部(在http协议中，数据包分为两部分，一部分是头部，由一些名值对构成，一部分是主体(body)，是真正传办理的数据（如HTML页面等）），必须以US-ASCII编码，这是因为头部不传数据而只描述被要传输的数据的一些信息，一个例外是cookie，它是数据但是通过头部进行传输的，所以它也要用US-ASCII编码。<br />
　　HTTP数据包的主体部分，可以用任何一种方式进行编码，默认是ISO-8859-1，具体可以用头部字段Content-Type指定。可以利用 addRequestHeader方法，设定编码方式；用 getResponseCharSet取得编码方式。对HTML或XML等类型的文档，它们的本身的Content-Type也可以指定编码方式，主要区分两者的作用范围以得到正确实的解码。<br />
　　URL的编码标准，由RFC1738指定为，只能是由可打印8位/字节的us-ascii<a href="http://www.myfaq.com.cn/Dev/Programme/VC/Str/index.html" target="_blank">字符</a>组成，80-ff不是us-ascii<a href="http://www.myfaq.com.cn/Dev/Programme/VC/Str/index.html" target="_blank">字符</a>，而00-1F是控制<a href="http://www.myfaq.com.cn/Dev/Programme/VC/Str/index.html" target="_blank">字符</a>，这两个区域中用的<a href="http://www.myfaq.com.cn/Dev/Programme/VC/Str/index.html" target="_blank">字符</a>都须加以编码(encoded)。<br />
　　
<h4>8、Cookies</h4>
<br />
　　 HttpClient能自动管理cookie,包括允许<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>设置cookie并在需要的时候自动将cookie返回<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>，它也支持手工设置cookie后发送到<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>端。不幸的是，对如何处理cookie，有几个规范互相冲突：Netscape Cookie 草案, RFC2109, RFC2965，而且还有很大数量的软件商的cookie实现不遵循任何规范. 为了处理这种状况，HttpClient提供了策略驱动的cookie管理方式。HttpClient支持的cookie规范有：
<ol>
    <li><a href="http://wp.netscape.com/newsref/std/cookie_spec.html">Netscape cookie草案</a>，是最早的cookie规范，基于rfc2109。尽管这个规范与rc2109有较大的差别，这样做可以与一些<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>兼容。 <br />
    <li><a title="cookie specification" href="http://www.w3.org/Protocols/rfc2109/rfc2109.txt">rfc2109</a>，是w3c发布的第一个官方cookie规范。理论上讲，所有的<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>在处理cookie(版本1)时，都要遵循此规范，正因如此，HttpClient将其设为默认的规范。遗憾的是，这个规范太严格了，以致很多<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>不正确的实施了该规范或仍在作用Netscape规范。在这种情况下，应使用兼容规范。<br />
    <li>兼容性规范，设计用来兼容尽可能多的<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>，即使它们并没有遵循标准规范。当解析cookie出现问题时，应考虑采用兼容性规范。</li>
</ol>
<br />
　　 RFC2965规范暂时没有被HttpClient支持（在以后的版本为会加上），它定义了cookie版本2，并说明了版本1cookie的不足，RFC2965有意有久取代rfc2109.<br />
　　在HttpClient中，有两种方法来指定cookie规范的使用，
<ol>
    <li>
    <div class="code">HttpClient client = new HttpClient();<br />
    client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);</div>
    这种方法设置的规范只对当前的HttpState有效，参数可取值CookiePolicy.COMPATIBILITY，CookiePolicy.NETSCAPE_DRAFT或CookiePolicy.RFC2109。<br />
    <li>
    <div class="code">System.setProperty("apache.commons.httpclient.cookiespec", "COMPATIBILITY");</div>
    此法指的规范，对以后每个新建立的HttpState对象都有效，参数可取值"COMPATIBILITY","NETSCAPE_DRAFT"或"RFC2109"。<br />
    　　常有不能解析cookie的问题，但更换到兼容规范大都能解决。<br />
    </li>
</ol>
　　
<h4>9、使用HttpClient遇到问题怎么办?</h4>
<br />
<ol>
    <li>用一个浏览器访问<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>，以确认<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>应答正常<br />
    <li>如果在使代理，关掉代理试试<br />
    <li>另找一个<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>来试试（如果运行着不同的<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>软件更好）<br />
    <li>检查代码是否按教程中讲的思路编写<br />
    <li>设置log级别为debug，找出问题出现的原因<br />
    <li>打开wiretrace，来追踪客户端与<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>的通信，以确实问题出现在什么地方<br />
    <li>用telnet或netcat手工将信息发送到<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>，适合于猜测已经找到了原因而进行试验时<br />
    <li>将netcat以监听方式运行，用作<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>以检查httpclient如何处理应答的。<br />
    <li>利用最新的httpclient试试，bug可能在最新的版本中修复了<br />
    <li>向邮件列表求帮助<br />
    <li>向bugzilla报告bug. </li>
</ol>
　　
<h4>10、SSL</h4>
<br />
　　借助Java Secure Socket Extension (JSSE)，HttpClient全面支持Secure Sockets Layer (SSL)或IETF Transport Layer Security (TLS)协议上的HTTP。JSSE已经jre1.4及以后的版本中，以前的版本则需要手工安装设置，具体过程参见<a href="http://<a%20href=/" target="_blank" http: www.myfaq.com.cn Dev Programme Java index.html?>java</a>.sun.com/products/jsse/doc/guide/API_users_guide.html#Installation"&gt;Sun网站</a>或本学习笔记。<br />
　　HttpClient中使用SSL非常简单，参考下面两个例子:
<div class="code">HttpClient httpclient = new HttpClient();<br />
GetMethod httpget = new GetMethod("https://www.verisign.com/"); <br />
httpclient.executeMethod(httpget);<br />
System.out.println(httpget.getStatusLine().toString());</div>
，如果通过需要授权的代理，则如下：
<div class="code">HttpClient httpclient = new HttpClient();<br />
httpclient.getHostConfiguration().setProxy("myproxyhost", 8080);<br />
httpclient.getState().setProxyCredentials("my-proxy-realm", " myproxyhost",<br />
new UsernamePasswordCredentials("my-proxy-username", "my-proxy-password"));<br />
GetMethod httpget = new GetMethod("https://www.verisign.com/"); <br />
httpclient.executeMethod(httpget);<br />
System.out.println(httpget.getStatusLine().toString());</div>
<br />
　　在HttpClient中定制SSL的步骤如下：<br />
<ol>
    <li>提供了一个实现了org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory接口的socket factory。这个 socket factory负责打一个到<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>的端口，使用标准的或第三方的SSL函数库，并进行象连接握手等初始化操作。通常情况下，这个初始化操作在端口被创建时自动进行的。<br />
    <li>实例化一个org.apache.commons.httpclient.protocol.Protocol对象。创建这个实例时，需要一个合法的协议类型(如https)，一个定制的socket factory，和一个默认的端中号(如https的443端口).
    <div class="code">Protocol myhttps = new Protocol("https", new MySSLSocketFactory(), 443);</div>
    然后，这个实例可被设置为协议的处理器。
    <div class="code">HttpClient httpclient = new HttpClient();<br />
    httpclient.getHostConfiguration().setHost("www.whatever.com", 443, myhttps);<br />
    GetMethod httpget = new GetMethod("/");<br />
    httpclient.executeMethod(httpget);</div>
    <br />
    <li>通过调用Protocol.registerProtocol方法，将此定制的实例，注册为某一特定协议的默认的处理器。由此，可以很方便地定制自己的协议类型(如myhttps)。
    <div class="code">Protocol.registerProtocol("myhttps", <br />
    new Protocol("https", new MySSLSocketFactory(), 9443));<br />
    ...<br />
    HttpClient httpclient = new HttpClient();<br />
    GetMethod httpget = new GetMethod("myhttps://www.whatever.com/");<br />
    httpclient.executeMethod(httpget);</div>
    如果想用自己定制的处理器取代https默认的处理器，只需要将其注册为"https"即可。
    <div class="code">Protocol.registerProtocol("https", <br />
    new Protocol("https", new MySSLSocketFactory(), 443));<br />
    HttpClient httpclient = new HttpClient();<br />
    GetMethod httpget = new GetMethod("https://www.whatever.com/");<br />
    httpclient.executeMethod(httpget);</div>
    </li>
</ol>
<br />
　　已知的限制和问题<br />
<ol>
    <li>持续的SSL连接在Sun的低于1.4JVM上不能工作，这是由于JVM的bug造成。<br />
    <li>通过代理访问<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>时，非抢先<a href="http://www.myfaq.com.cn/RenZ/index.html" target="_blank">认证</a>（ Non-preemptive authentication）会失败，这是由于HttpClient的设计缺陷造成的，以后的版本中会修改。</li>
</ol>
<br />
　　遇到问题的处理<br />
　　很多问题，特别是在jvm低于1.4时，是由jsse的安装造成的。<br />
　　下面的代码，可作为最终的检测手段。<br />
<div class="code">import <a href="http://www.myfaq.com.cn/Dev/Programme/Java/index.html" target="_blank">java</a>.io.BufferedReader;<br />
import <a href="http://www.myfaq.com.cn/Dev/Programme/Java/index.html" target="_blank">java</a>.io.InputStreamReader;<br />
import <a href="http://www.myfaq.com.cn/Dev/Programme/Java/index.html" target="_blank">java</a>.io.OutputStreamWriter;<br />
import <a href="http://www.myfaq.com.cn/Dev/Programme/Java/index.html" target="_blank">java</a>.io.Writer;<br />
import <a href="http://www.myfaq.com.cn/Dev/Programme/Java/index.html" target="_blank">java</a>.net.Socket; import <a href="http://www.myfaq.com.cn/Dev/Programme/Java/index.html" target="_blank">java</a>x.net.ssl.SSLSocketFactory; public class Test {<br />
<br />
public static final String TARGET_HTTPS_SERVER = "www.verisign.com"; <br />
public static final int TARGET_HTTPS_PORT = 443; <br />
<br />
public static void main(String[] args) throws Exception {<br />
<br />
Socket socket = SSLSocketFactory.getDefault().<br />
createSocket(TARGET_HTTPS_SERVER, TARGET_HTTPS_PORT);<br />
try {<br />
Writer out = new OutputStreamWriter(<br />
socket.getOutputStream(), "ISO-8859-1");<br />
out.write("GET / HTTP/1.1\r\n"); <br />
out.write("Host: " + TARGET_HTTPS_SERVER + ":" + <br />
TARGET_HTTPS_PORT + "\r\n"); <br />
out.write("Agent: SSL-TEST\r\n"); <br />
out.write("\r\n"); <br />
out.flush(); <br />
BufferedReader in = new BufferedReader(<br />
new InputStreamReader(socket.getInputStream(), "ISO-8859-1"));<br />
String line = null;<br />
while ((line = in.readLine()) != null) {<br />
System.out.println(line);<br />
}<br />
} finally {<br />
socket.close(); <br />
}<br />
}<br />
}</div>
<br />
　　
<h4>11、httpclient的多线程处理</h4>
<br />
　　使用多线程的主要目的，是为了实现并行的下载。在httpclient运行的过程中，每个http协议的方法，使用一个HttpConnection实例。由于连接是一种有限的资源，每个连接在某一时刻只能供一个线程和方法使用，所以需要确保在需要时正确地分配连接。HttpClient采用了一种类似jdbc连接池的方法来管理连接，这个管理工作由 MultiThreadedHttpConnectionManager完成。
<div class="code">MultiThreadedHttpConnectionManager connectionManager = <br />
new MultiThreadedHttpConnectionManager();<br />
HttpClient client = new HttpClient(connectionManager);</div>
此是，client可以在多个线程中被用来执行多个方法。每次调用HttpClient.executeMethod() 方法，都会去链接管理器申请一个连接实例，申请成功这个链接实例被签出(checkout)，随之在链接使用完后必须归还管理器。管理器支持两个设置：
<table border="1">
    <tbody>
        <tr>
            <td>maxConnectionsPerHost</td>
            <td>每个主机的最大并行链接数，默认为2</td>
        </tr>
        <tr>
            <td>maxTotalConnections</td>
            <td>客户端总并行链接最大数，默认为20</td>
        </tr>
    </tbody>
</table>
<br />
　　管理器重新利用链接时，采取早归还者先重用的方式（least recently used approach）。<br />
　　由于是使用HttpClient的<a href="http://www.myfaq.com.cn/Dev/index.html" target="_blank">程序</a>而不是HttpClient本身来读取应答包的主体，所以HttpClient无法决定什么时间连接不再使用了，这也就要求在读完应答包的主体后必须手工显式地调用releaseConnection()来释放申请的链接。
<div class="code">MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();<br />
HttpClient client = new HttpClient(connectionManager);<br />
...<br />
// 在某个线程中。<br />
GetMethod get = new GetMethod("http://jakarta.apache.org/");<br />
try {<br />
client.executeMethod(get);<br />
// print response to stdout<br />
System.out.println(get.getResponseBodyAsStream());<br />
} finally {<br />
// be sure the connection is released back to the connection <br />
// manager<br />
get.releaseConnection();<br />
}</div>
对每一个HttpClient.executeMethod须有一个method.releaseConnection()与之匹配.
<h4>12、HTTP方法</h4>
<br />
　　HttpClient支持的HTTP方法有8种，下面分述之。<br />
<br />
　　1、Options<br />
<br />
　　HTTP方法Options用来向<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>发送请求，希望获得针对由请求URL(request url)标志的资源在请求/应答的通信过程可以使用的功能选项。通过这个方法，客户端可以在采取具体行动之前，就可对某一资源决定采取什么动作和/或以及一些必要条件，或者了解<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>提供的功能。这个方法最典型的应用，就是用来获取<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>支持哪些HTTP方法。<br />
　　HttpClient中有一个类叫OptionsMethod，来支持这个HTTP方法，利用这个类的getAllowedMethods方法，就可以很简单地实现上述的典型应用。<br />
<div class="code">OptionsMethod options = new OptionsMethod("http://jakarta.apache.org");<br />
// 执行方法并做相应的异常处理<br />
...<br />
Enumeration allowedMethods = options.getAllowedMethods();<br />
options.releaseConnection();</div>
<br />
　　2、Get<br />
<br />
　　 HTTP方法GET用来取回请求URI（request-URI）标志的任何信息（以实体(entity)的形式），"get"这个单词本意就是&#8221;获取&#8220;的意思。如果请求URI指向的一个数据处理过程，那这个过程生成的数据，在应答中以实体的形式被返回，而不是将这个过程的代码的返回。<br />
　　如果HTTP包中含有If-ModifiedSince, If-Unmodified-Since, If-Match, If-None-Match, 或 If-Range等头字段，则GET也就变成了&#8221;条件GET&#8220;，即只有满足上述字段描述的条件的实体才被取回，这样可以减少一些非必需的<a href="http://www.myfaq.com.cn/Net/index.html" target="_blank">网络</a>传输，或者减少为获取某一资源的多次请求（如第一次检查，第二次下载）。（一般的浏览器，都有一个临时目录，用来缓存一些网页信息，当再次浏览某个页面的时候，只下载那些修改过的内容，以加快浏览速度，就是这个道理。至于检查，则常用比GET更好的方法HEAD来实现。）如果HTTP包中含有Range头字段，那么请求URI指定的实体中，只有决定范围条件的那部分才被取回来。（用过多线程下载工具的朋友，可能比较容易理解这一点）<br />
　　这个方法的典型应用，用来从web<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>下载文档。HttpClient定义了一个类叫GetMethod来支持这个方法，用GetMethod类中getResponseBody, getResponseBodyAsStream 或 getResponseBodyAsString函数就可以取到应答包包体中的文档（如HTML页面）信息。这这三个函数中，getResponseBodyAsStream通常是最好的方法，主要是因为它可以避免在处理下载的文档之前缓存所有的下载的数据。<br />
<div class="code">GetMethod get = new GetMethod("http://jakarta.apache.org");<br />
// 执行方法，并处理失败的请求.<br />
...<br />
InputStream in = get.getResponseBodyAsStream();<br />
// 利用输入流来处理信息。<br />
get.releaseConnection();</div>
<br />
　　对GetMethod的最常见的不正确的使用，是没有将全部的应答主体的数据读出来。还有，必须注意要手工明确地将链接释放。<br />
<br />
　　3、Head<br />
<br />
　　HTTP的Head方法，与Get方法完全一致，唯一的差别是<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>不能在应答包中包含主体(message-body)，而且一定不能包含主体。使用这个方法，可以使得客户无需将资源下载回就可就以得到一些关于它的基本信息。这个方法常用来检查超链的可访问性以及资源最近有没有被修改。<br />
　　HTTP的head方法最典型的应用，是获取资源的基本信息。HttpClient定义了HeadMethod类支持这个方法，HeadMethod类与其它*Method类一样，用 getResponseHeaders()取回头部信息，而没有自己的特殊方法。<br />
<div class="code">HeadMethod head = new HeadMethod("http://jakarta.apache.org");<br />
// 执行方法，并处理失败的请求.<br />
...<br />
// 取回应答包的头字段信息.<br />
Header[] headers = head.getResponseHeaders(); // 只取回最后修改日期字段的信息.<br />
String lastModified = head.getResponseHeader("last-modified").getValue();</div>
<br />
<br />
　　4、Post<br />
<br />
　　Post在英文有&#8220;派驻&#8221;的意思，HTTP方法POST就是要求<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>接受请求包中的实体，并将其作为请求URI的下属资源。从本质上说，这意味着<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>要保存这个实体信息，而且通常由<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>端的<a href="http://www.myfaq.com.cn/Dev/index.html" target="_blank">程序</a>进行处理。Post方法的设计意图，是要以一种统一的方式实现下列功能：
<ol>
    <li>对已有的资源做评注<br />
    <li>将信息发布到BBS、新闻组、邮件列表，或类似的文章组中<br />
    <li>将一块数据，提交给数据处理进程<br />
    <li>通过追加操作，来扩展一个<a href="http://www.myfaq.com.cn/Dev/DataBase/index.html" target="_blank">数据库</a></li>
</ol>
　　这些都操作期待着在<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>端产生一定的&#8220;副作用&#8221;，如修改了<a href="http://www.myfaq.com.cn/Dev/DataBase/index.html" target="_blank">数据库</a>等。<br />
　　HttpClient定义PostMethod类以支持该HTTP方法，在httpclient中，使用post方法有两个基本的步骤：为请求包准备数据，然后读取<a href="http://www.myfaq.com.cn/System/Server/index.html" target="_blank">服务器</a>来的应答包的信息。通过调用 setRequestBody()函数，来为请求包提供数据，它可以接收三类参数：输入流、名值对数组或<a href="http://www.myfaq.com.cn/Dev/Programme/VC/Str/index.html" target="_blank">字符</a>串。至于读取应答包需要调用 getResponseBody* 那一系列的方法，与GET方法处理应答包的方法相同。<br />
　　常见问题是，没有将全部应答读取（无论它对<a href="http://www.myfaq.com.cn/Dev/index.html" target="_blank">程序</a>是否有用），或没有释放链接资源。
<img src ="http://www.blogjava.net/liuzheng/aggbug/180506.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-02-18 17:49 <a href="http://www.blogjava.net/liuzheng/articles/180506.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring--简单使用quartz实现定时作业 （转）</title><link>http://www.blogjava.net/liuzheng/articles/179946.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Thu, 14 Feb 2008 08:11:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/179946.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/179946.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/179946.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/179946.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/179946.html</trackback:ping><description><![CDATA[&nbsp;定时批处理作业是J2EE企业应用里很重要的一环，用来在晚间进行财务挂账，数据转存，新闻联播等等操作。
<p>&nbsp;&nbsp;&nbsp; 而在Spring里，已经很好的集成了Quartz，简单到像配cron一样，在xml文件里面配一下时间就可以自动执行，不需要写一行代码。Spring对Quartz大刀阔斧的简化堪称范例，Quartz项目组也许可以学习一下。</p>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean id="methodInvokingJobDetail"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="targetObject"&gt;&lt;ref bean="financeDAO"/&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="targetMethod"&gt;&lt;value&gt;confirmOrder&lt;/value&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<pre><br />
&nbsp;&nbsp;&nbsp; &lt;bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="jobDetail"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="methodInvokingJobDetail"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cronExpression"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;0 0 6,12,20 * * ?&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<pre>&nbsp;&nbsp;&nbsp; &lt;bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="triggers"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;list&gt;&lt;ref local="cronTrigger"/&gt;&lt;/list&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</pre>
<p>上面这段配置文件规定了在早上6点和晚上8点执行financeDAO对象的confirmOrder()方法.<br />
</p>
<p><br />
附：cronExpression配置说明</p>
<p>
<table cellspacing="8">
    <tbody>
        <tr>
            <th align="left">字段</th>
            <th align="left">&nbsp;</th>
            <th align="left">允许值</th>
            <th align="left">&nbsp;</th>
            <th align="left">允许的特殊字符</th>
        </tr>
        <tr>
            <td align="left"><code>秒</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>0-59</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>, - * /</code></td>
        </tr>
        <tr>
            <td align="left"><code>分</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>0-59</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>, - * /</code></td>
        </tr>
        <tr>
            <td align="left"><code>小时</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>0-23</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>, - * /</code></td>
        </tr>
        <tr>
            <td align="left"><code>日期</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>1-31</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>, - * ? / L W C</code></td>
        </tr>
        <tr>
            <td align="left"><code>月份</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>1-12&nbsp;或者 JAN-DEC</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>, - * /</code></td>
        </tr>
        <tr>
            <td align="left"><code>星期</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>1-7&nbsp;或者 SUN-SAT</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>, - * ? / L C #</code></td>
        </tr>
        <tr>
            <td align="left"><code>年（可选）</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>留空, 1970-2099</code></td>
            <td align="left">&nbsp; </td>
            <td align="left"><code>, - * /</code></td>
        </tr>
    </tbody>
</table>
</p>
<img src ="http://www.blogjava.net/liuzheng/aggbug/179946.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-02-14 16:11 <a href="http://www.blogjava.net/liuzheng/articles/179946.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring2.0中新标签的使用（转）</title><link>http://www.blogjava.net/liuzheng/articles/179945.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Thu, 14 Feb 2008 08:09:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/179945.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/179945.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/179945.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/179945.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/179945.html</trackback:ping><description><![CDATA[1,&lt;util:constant/&gt;<br />
取代了之前通过FieldRetrevingFactoryBean获取bean静态常量的FactoryBean！<br />
<br />
<br />
老的用法<br />
[code]&lt;bean id="..." class="..."&gt;<br />
&nbsp; &lt;property name="isolation"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"<br />
&nbsp;&nbsp;&nbsp; class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" /&gt;<br />
&nbsp; &lt;/property&gt;<br />
&lt;/bean&gt;[/code]<br />
<br />
新用法<br />
[code]&lt;bean id="..." class="..."&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;property name="isolation"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br />
&lt;/bean&gt;[/code]<br />
<br />
2,&lt;util:property-path/&gt;<br />
取代了之前通过PropertyPathFactoryBean来指定属性路径获取属性值的FactoryBean！<br />
<br />
老的用法<br />
[code]<br />
&lt;bean id="testBean" class="org.springframework.beans.TestBean" singleton="false"&gt;<br />
&nbsp; &lt;property name="age" value="10"/&gt;<br />
&nbsp; &lt;property name="spouse"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;bean class="org.springframework.beans.TestBean"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="age" value="11"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp; &lt;/property&gt;<br />
&lt;/bean&gt;<br />
<br />
&lt;bean id="testBean.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/&gt;[/code]<br />
<br />
<br />
新的用法<br />
[code]<br />
&lt;bean id="testBean" class="org.springframework.beans.TestBean" singleton="false"&gt;<br />
&nbsp; &lt;property name="age" value="10"/&gt;<br />
&nbsp; &lt;property name="spouse"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;bean class="org.springframework.beans.TestBean"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="age" value="11"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br />
&nbsp; &lt;/property&gt;<br />
&lt;/bean&gt;<br />
<br />
&lt;util:property-path id="name" path="testBean.age"/&gt;<br />
[/code]<br />
<br />
<br />
3,&lt;util:properties/&gt;<br />
替代了之前通过PropertiesFactoryBean来获取properties配制文件数据的FactoryBean!<br />
<br />
<br />
老用法<br />
[code]&lt;bean id="jdbcConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean"&gt;<br />
&nbsp; &lt;property name="location" value="classpath:com/foo/jdbc-production.properties"/&gt;<br />
&lt;/bean&gt;[/code]<br />
<br />
<br />
新用法<br />
[code]&lt;util:properties id="jdbcConfiguration" location="classpath:com/foo/jdbc-production.properties"/&gt;[/code]<br />
<br />
4,&lt;util:list/&gt;<br />
替代了之前通过&lt;list&gt;&lt;value&gt;&lt;/value&gt;&lt;/list&gt;的内置配制方式，让多个bean调用同一list成为可能！<br />
<br />
老的用法<br />
[code]&lt;bean id="emails" class="org.springframework.beans.factory.config.ListFactoryBean"&gt;<br />
&nbsp; &lt;property name="sourceList"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;list&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;pechorin@hero.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;raskolnikov@slums.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;stavrogin@gov.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;porfiry@gov.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br />
&nbsp; &lt;/property&gt;<br />
&lt;/bean&gt;[/code]<br />
<br />
新的用法<br />
[code]&lt;util:list id="emails" list-class="java.util.LinkedList"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;value&gt;pechorin@hero.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;value&gt;raskolnikov@slums.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;value&gt;stavrogin@gov.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;value&gt;porfiry@gov.org&lt;/value&gt;<br />
&lt;/util:list&gt;[/code]<br />
<br />
5,&lt;util:map/&gt;<br />
替代了之前适用&lt;map&gt;&lt;entry key=""&gt;&lt;value&gt;&lt;/value&gt;&lt;/entry&gt;&lt;/map&gt;的配置方式，让多个bean调用同一map成为可能！<br />
<br />
老的用法<br />
[code]&lt;bean id="emails" class="org.springframework.beans.factory.config.MapFactoryBean"&gt;<br />
&nbsp; &lt;property name="sourceMap"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;map&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;entry key="pechorin" value="pechorin@hero.org"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;entry key="raskolnikov" value="raskolnikov@slums.org"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;entry key="stavrogin" value="stavrogin@gov.org"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;entry key="porfiry" value="porfiry@gov.org"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br />
&nbsp; &lt;/property&gt;<br />
&lt;/bean&gt;[/code]<br />
<br />
新的用法<br />
[code]&lt;util:map id="emails" map-class="java.util.TreeMap"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;entry key="pechorin" value="pechorin@hero.org"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;entry key="raskolnikov" value="raskolnikov@slums.org"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;entry key="stavrogin" value="stavrogin@gov.org"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;entry key="porfiry" value="porfiry@gov.org"/&gt;<br />
&lt;/util:map&gt;[/code]<br />
<br />
<br />
6,&lt;util:set/&gt;<br />
替代了之前适用&lt;set&gt;&lt;value&gt;&lt;/value&gt;&lt;/set&gt;的配置方式，<br />
让多个bean调用同一set成为可能！<br />
<br />
老的用法<br />
[code]&lt;bean id="emails" class="org.springframework.beans.factory.config.SetFactoryBean"&gt;<br />
&nbsp; &lt;property name="sourceSet"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;set&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;pechorin@hero.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;raskolnikov@slums.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;stavrogin@gov.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;porfiry@gov.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/set&gt;<br />
&nbsp; &lt;/property&gt;<br />
&lt;/bean&gt;[/code]<br />
<br />
<br />
新的用法<br />
[code]&lt;util:set id="emails" set-class="java.util.TreeSet"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;value&gt;pechorin@hero.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;value&gt;raskolnikov@slums.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;value&gt;stavrogin@gov.org&lt;/value&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;value&gt;porfiry@gov.org&lt;/value&gt;<br />
&lt;/util:set&gt;[/code]<br />
<br />
7,&lt;jee:jndi-lookup/&gt;<br />
替代了之前使用JndiObjectFactoryBean指定配制获取容器JNDI资源的FactoryBean!<br />
<br />
老的用法<br />
[code]&lt;bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;property name="jndiName" value="jdbc/MyDataSource"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;property name="cache" value="true"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;property name="resourceRef" value="true"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;property name="lookupOnStartup" value="false"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;property name="expectedType" value="com.myapp.DefaultFoo"/&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;property name="proxyInterface" value="com.myapp.Foo"/&gt;<br />
&lt;/bean&gt;<br />
[/code]<br />
<br />
新用法<br />
[code]&lt;jee:jndi-lookup id="simple"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jndi-name="jdbc/MyDataSource"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cache="true"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resource-ref="true"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lookup-on-startup="false"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expected-type="com.myapp.DefaultFoo"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy-interface="com.myapp.Foo"/&gt;[/code]<br />
<br />
8,&lt;jee:local-slsb/&gt;<br />
替代了之前使用LocalStatelessSessionProxyFactoryBean来获取无状态会话BEAN的FactoryBean!<br />
<br />
老的用法<br />
[code]&lt;bean id="complexRemoteEjb"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean"&gt;<br />
&nbsp; &lt;property name="jndiName" value="ejb/MyRemoteBean"/&gt;<br />
&nbsp; &lt;property name="businessInterface" value="com.foo.service.RentalService"/&gt;<br />
&nbsp; &lt;property name="cacheHome" value="true"/&gt;<br />
&nbsp; &lt;property name="lookupHomeOnStartup" value="true"/&gt;<br />
&nbsp; &lt;property name="resourceRef" value="true"/&gt;<br />
&nbsp; &lt;property name="homeInterface" value="com.foo.service.RentalService"/&gt;<br />
&nbsp; &lt;property name="refreshHomeOnConnectFailure" value="true"/&gt;<br />
&lt;/bean&gt;[/code]<br />
<br />
新的用法<br />
[code]&lt;jee:remote-slsb id="complexRemoteEjb"<br />
&nbsp;&nbsp;&nbsp; jndi-name="ejb/MyRemoteBean"<br />
&nbsp;&nbsp;&nbsp; business-interface="com.foo.service.RentalService"<br />
&nbsp;&nbsp;&nbsp; cache-home="true"<br />
&nbsp;&nbsp;&nbsp; lookup-home-on-startup="true"<br />
&nbsp;&nbsp;&nbsp; resource-ref="true"<br />
&nbsp;&nbsp;&nbsp; home-interface="com.foo.service.RentalService"<br />
&nbsp;&nbsp;&nbsp; refresh-home-on-connect-failure="true"&gt;[/code]<br />
<br />
&nbsp;
<img src ="http://www.blogjava.net/liuzheng/aggbug/179945.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-02-14 16:09 <a href="http://www.blogjava.net/liuzheng/articles/179945.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用command-chain完成的For循环</title><link>http://www.blogjava.net/liuzheng/articles/176927.html</link><dc:creator>刘铮 </dc:creator><author>刘铮 </author><pubDate>Tue, 22 Jan 2008 02:24:00 GMT</pubDate><guid>http://www.blogjava.net/liuzheng/articles/176927.html</guid><wfw:comment>http://www.blogjava.net/liuzheng/comments/176927.html</wfw:comment><comments>http://www.blogjava.net/liuzheng/articles/176927.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liuzheng/comments/commentRss/176927.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liuzheng/services/trackbacks/176927.html</trackback:ping><description><![CDATA[<p>package nl.enovation.commons.command;</p>
<p>import java.util.Collection;</p>
<p>import org.apache.commons.chain.Command;<br />
import org.apache.commons.chain.Context;</p>
<p>public class ForeachCommand implements Command {<br />
&nbsp;&nbsp;&nbsp; private String collectionProperty;<br />
&nbsp;&nbsp;&nbsp; private String instanceProperty;<br />
&nbsp;&nbsp;&nbsp; private Command command;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; @SuppressWarnings("unchecked")<br />
&nbsp;&nbsp;&nbsp; public boolean execute(Context context) throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Collection&lt;? extends Object&gt; collection = (Collection&lt;? extends Object&gt;) context.get(collectionProperty);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (collection != null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(Object object : collection) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context.put(instanceProperty, object);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (PROCESSING_COMPLETE == command.execute(context)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return PROCESSING_COMPLETE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } // else continue<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return CONTINUE_PROCESSING;<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public String getCollectionProperty() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return collectionProperty;<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public void setCollectionProperty(String collectionProperty) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.collectionProperty = collectionProperty;<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public String getInstanceProperty() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return instanceProperty;<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public void setInstanceProperty(String instanceProperty) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.instanceProperty = instanceProperty;<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public Command getCommand() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return command;<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public void setCommand(Command command) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.command = command;<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>}<br />
</p>
<img src ="http://www.blogjava.net/liuzheng/aggbug/176927.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liuzheng/" target="_blank">刘铮 </a> 2008-01-22 10:24 <a href="http://www.blogjava.net/liuzheng/articles/176927.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>