﻿<?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-澹泊明志 宁静致远 从容淡定 宠辱不惊-文章分类-JAVA</title><link>http://www.blogjava.net/zphab/category/41731.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 19 Sep 2009 04:58:26 GMT</lastBuildDate><pubDate>Sat, 19 Sep 2009 04:58:26 GMT</pubDate><ttl>60</ttl><item><title>Apache Common BeanUtils</title><link>http://www.blogjava.net/zphab/articles/295632.html</link><dc:creator>张平辉</dc:creator><author>张平辉</author><pubDate>Fri, 18 Sep 2009 16:16:00 GMT</pubDate><guid>http://www.blogjava.net/zphab/articles/295632.html</guid><wfw:comment>http://www.blogjava.net/zphab/comments/295632.html</wfw:comment><comments>http://www.blogjava.net/zphab/articles/295632.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zphab/comments/commentRss/295632.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zphab/services/trackbacks/295632.html</trackback:ping><description><![CDATA[<p><font color="#ff0000"><strong>目前 Commons 简介</strong></font><br />
&nbsp;&nbsp;&nbsp;&nbsp; 目前已经开发有 release 出来的版本有 BeanUtils, Betwixt, CLI, Collections, DBCP, Digester, Discovery, EL, FileUpload, HttpClient, Jelly, Jexl, JXPath, Lang, Latka, Logging, Modeler, Net, Pool, Validator 等等&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 每个版本都不太一样, 随时都有更新的可能, 至于还没有 release 出来正式的版本, 有一些项目, 可能也正在 使用了 !! 也是有可能因为其他项目做出来的一些元件, 可以抽出来共用的, 例如目前 struts 用的 Resources ( Resource bundle component ) , 也被列入 SandBox 研发中, 准备 release 更符合所有项目的 组件.&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; jakarta 为何要有 commons 这个 project 出现, 就是希望大家不用重复开发一样的组件, 达到 reusable 的 目的 !! 而且他们都有容易使用的特性, 也是各个 jakarta committer 牛人们的精华杰作, 因此, 绝对不能错 过这一个 open source project !! 各位亲爱的 java 同胞们 ................. </p>
<p><font color="#ff0000"><strong>BeanUtils 介绍</strong></font><br />
&nbsp;&nbsp;&nbsp;&nbsp; 当我在选择要先介绍哪一个组件, 实在犹豫了很久, 因为每一个实在都是精华, 久久无法做出决定, 所以呢, 只好按照是否 release 再按照字母的先后, 做一个排序, 希望大家明白 .... <br />
<br />
所谓 BeanUtils 为何要开发呢, 每个工程师或许在写 JavaBean 的时候, 都会乖乖地去写 getters 和 setters, 就是 getXXX() 及 setXXX() methods, 但是当你的 object 是动态产生的, 也许是用文件, 也许是 其他原因, 那你该如何去存取数据呢 !!&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 几个例子你可能会用到 BeanUtils, 当然, 这是已经存在的项目了 </p>
<ul>
    <li><a href="http://jakarta.apache.org/bsf/index.html" target="_blank"><font color="#000000">BSF</font></a> : Script Language 和 Java Object Model 之间
    <li><a href="http://jakarta.apache.org/velocity/index.html" target="_blank"><font color="#000000">Velocity</font></a>/<a href="http://java.sun.com/products/jsp/" target="_blank"><font color="#000000">JSP</font></a> : 使用 template 建立相似的网页
    <li><a href="http://jakarta.apache.org/taglibs/index.html" target="_blank"><font color="#000000">jakarta taglibs</font></a>/ <a href="http://jakarta.apache.org/struts/index.html" target="_blank"><font color="#000000">Struts</font></a> / <a href="http://xml.apache.org/cocoon/index.html" target="_blank"><font color="#000000">Cocoon</font></a> : 建立自己特殊的 Tag Libraries for JSP 或 XSP
    <li><a href="http://ant.apache.org/" target="_blank"><font color="#000000">ant build.xml</font></a> / <a href="http://jakarta.apache.org/tomcat/index.html" target="_blank"><font color="#000000">tomcat server.xml</font></a> : XML-based 的配置文件 ( configuration resources ) </li>
</ul>
&nbsp;&nbsp;&nbsp;&nbsp; 你大可以使用 java api 中的 java.lang.reflect 及 java.beans 来达到这些数据交换 ~~ 不过呢, 难度有点 高 =.="" ,但是, BeanUtils 将会减低你开发的时间 !!&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 目前最新的版本为 1.6.1 (2003/2/18 released), 下载位置为 <a href="http://pluto.cdpa.nsysu.edu.tw/apache/dist/jakarta/commons/beanutils/binaries/" target="_blank"><font color="#000000">Binary </font></a>&amp; <a href="http://pluto.cdpa.nsysu.edu.tw/apache/dist/jakarta/commons/beanutils/source/" target="_blank"><font color="#000000">Source </font></a><br />
<br />
<p><font color="#ff0000"><strong>BeanUtils API 介绍</strong></font><br />
&nbsp;&nbsp;&nbsp;&nbsp; BeanUtils 的 <a href="http://jakarta.apache.org/commons/beanutils/api/index.html" target="_blank"><font color="#000000">Java API</font></a> 主要的 package 总共四项 <br />
<br />
</p>
<ol>
    <li>org.apache.commons.beanutils
    <li>org.apache.commons.beanutils.converters
    <li>org.apache.commons.beanutils.locale
    <li>org.apache.commons.beanutils.locale.converters </li>
</ol>
&nbsp;&nbsp;&nbsp;&nbsp; 其实除了第一项之外, 其他的都是后来的版本才加上去的, converters 就是专门处理不同传入的 object 该如 何转换, locale 呢, 就是为了国际化的处理, 所以重点我都会摆在第一项!!<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 而其中最常用到的 class 是 PropertyUtils 及 ConvertUtils 还有 DynaBeans( 有用 struts dynaform 的应 该不陌生 ) <br />
<p><font color="#ff0000"><strong><br />
BeanUtils.PropertyUtils 介绍</strong></font><br />
&nbsp;&nbsp;&nbsp;&nbsp; 基本上, 我假设大家对 JavaBean 的开发都没有问题, 就是对 getters 及 setters 都了解是什么. 先假设, Employee class&nbsp;<code><br />
&nbsp;&nbsp;&nbsp;&nbsp;public class Employee {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public Address getAddress(String type);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void setAddress(String type, Address address);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public Employee getSubordinate(int index);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void setSubordinate(int index, Employee subordinate);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public String getFirstName();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void setFirstName(String firstName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public String getLastName();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void setLastName(String lastName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</code></p>
&nbsp;&nbsp;&nbsp;&nbsp; 在 PropertyUtils 中会区分为三种 method 状态<br />
<br />
<ul>
    <li><strong>Simple</strong> - 如果你是用到 primitive 语法, 如 int, String 或其他自行开发的 objects 等等, 只需要单一的对象就可以取得数据<br />
    <br />
    <font color="#0000ff">PropertyUtils.getSimpleProperty(Object bean, String name) <br />
    PropertyUtils.setSimpleProperty(Object bean, String name, Object value)&nbsp;</font>
    <li><code><br />
    Employee employee = ...;<br />
    String firstName = (String)PropertyUtils.getSimpleProperty(employee, "firstName");<br />
    String lastName = (String)PropertyUtils.getSimpleProperty(employee, "lastName");<br />
    .............<br />
    PropertyUtils.setSimpleProperty(employee, "firstName", firstName);<br />
    PropertyUtils.setSimpleProperty(employee, "lastName", lastName);<br />
    &nbsp;</code>
    <li><strong>Indexed</strong> - 如果你是用到 Collection 或 List 实作出来的 objects , 只需要使用一个 index 数值就可以取得对象的状态<br />
    <br />
    <font color="#0000ff">PropertyUtils.getIndexedProperty(Object bean, String name) <br />
    PropertyUtils.getIndexedProperty(Object bean, String name, int index) <br />
    PropertyUtils.setIndexedProperty(Object bean, String name, Object value) <br />
    PropertyUtils.setIndexedProperty(Object bean, String name, int index, Object value)&nbsp;</font><code><br />
    <br />
    Employee employee = ...;<br />
    int index = ...;<br />
    String name = "subordinate[" + index + "]";<br />
    Employee subordinate = (Employee)PropertyUtils.getIndexedProperty(employee, name);<br />
    Employee employee = ...;<br />
    int index = ...;<br />
    Employee subordinate = (Employee)PropertyUtils.getIndexedProperty(employee, "subordinate", index);<br />
    <br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code>
    <li><strong>Mapped</strong> - 如果你是用到 Map 延伸出來的 objects , 只需要使用一个 key 值就可以取 得数据<br />
    <br />
    <font color="#0000ff">PropertyUtils.getMappedProperty(Object bean, String name) <br />
    PropertyUtils.getMappedProperty(Object bean, String name, String key) <br />
    PropertyUtils.setMappedProperty(Object bean, String name, Object value) <br />
    PropertyUtils.setMappedProperty(Object bean, String name, String key, Object value)&nbsp;</font><code><br />
    <br />
    Employee employee = ...;<br />
    Address address = ...;<br />
    PropertyUtils.setMappedProperty(employee, "address(home)", address);<br />
    Employee employee = ...;<br />
    Address address = ...;<br />
    PropertyUtils.setMappedProperty(employee, "address", "home", address);&nbsp;&nbsp;&nbsp;</code></li>
</ul>
&nbsp;&nbsp;&nbsp;&nbsp; 但是如果你是巢状(nested)的数据结构, 你该如何取得你要的数据呢<br />
<br />
<font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PropertyUtils.getNestedProperty(Object bean, String name)&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PropertyUtils.setNestedProperty(Object bean, String name, Object value)&nbsp;<br />
<br />
</font>&nbsp;&nbsp;&nbsp;&nbsp; 你只需要简单地使用 ".", 就可以得到你要的数据了
<pre><code>&nbsp;&nbsp;&nbsp;&nbsp; String city = (String)PropertyUtils.getNestedProperty(employee, "address(home).city");<br />
</code></pre>
&nbsp;&nbsp;&nbsp;&nbsp; 千万要记住, BeanUtils 是要让你随心所欲使用, 所以呢 index , mapped 当然都可以这样使用&nbsp;<code><br />
&nbsp;&nbsp;&nbsp;&nbsp; Employee employee = ...;<br />
&nbsp;&nbsp;&nbsp;&nbsp; String city = (String) PropertyUtils.getProperty(employee, "subordinate[3].address(home).city");<br />
<br />
</code>
<p><font color="#ff0000"><strong>BeanUtils.DynaBean and BeanUtils.DynaClass 介绍</strong></font><br />
&nbsp;&nbsp;&nbsp;&nbsp; 所有的 Dynamic JavaBean 都是实现 DynaBean 或 DynaClass 这两个 interface, 也可能会用到 DynaProperty class 来存取 properties . 我们为何要用到 Dynamic JavaBean 呢, 例如, 你从数据库取出来 的数据, 有时候可能是三个栏位, 有时候是四个栏位, 如果我们对于每个 Bean 都要去写一个 class, 就会很 累, 所以对于每一种 javabean 我们就设定他的属性有哪些, 接着就可以使用 PropertyUtils 来将他的数值取 出, 如此, 可以减少很多开发工时. 在 Struts 的课程中, 很多人问到我, 请问每一个 ActionForm 都必须写 成 java 文件吗, 当然, 不需要的, 否则一个网页一个 ActionForm ( 假设都不一样 ), 那么, 这么浪费时间 的工作, 为何还要使用 Struts 来作为 Framework 呢, 所以我们都是使用 org.apache.struts.action.DynaActionForm!!<br />
<br />
<font color="#555555"><em>MutableDynaClass ( since $1.5 ) 这是蛮新的一个 DynaClass, 是为了动态可以调整 properties ! </em></font></p>
<ul>
    <li><strong>BasicDynaBean and BasicDynaClass</strong> - 基本的 Dynamic 类型 <br />
    <br />
    <font color="#0000ff">BasicDynaClass(java.lang.String name, java.lang.Class dynaBeanClass, DynaProperty[] properties) <br />
    BasicDynaBean(DynaClass dynaClass)&nbsp;</font><code><br />
    <br />
    DynaProperty[] props = new DynaProperty[]{<br />
    &nbsp;&nbsp;&nbsp;&nbsp;new DynaProperty("address", java.util.Map.class),<br />
    &nbsp;&nbsp;&nbsp;&nbsp;new DynaProperty("subordinate", mypackage.Employee[].class),<br />
    &nbsp;&nbsp;&nbsp;&nbsp;new DynaProperty("firstName", String.class),<br />
    &nbsp;&nbsp;&nbsp;&nbsp;new DynaProperty("lastName",&nbsp;&nbsp;String.class)<br />
    };<br />
    BasicDynaClass dynaClass = new BasicDynaClass("employee", null, props);<br />
    DynaBean employee = dynaClass.newInstance();<br />
    employee.set("address", new HashMap());<br />
    employee.set("subordinate", new mypackage.Employee[0]);<br />
    employee.set("firstName", "Fred");<br />
    employee.set("lastName", "Flintstone");<br />
    <br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code>
    <li><strong>ResultSetDynaClass (Wraps ResultSet in DynaBeans)</strong> - 使用 ResultSet 的 Dynamic JavaBean<br />
    <br />
    <font color="#0000ff">ResultSetDynaClass(java.sql.ResultSet resultSet)<br />
    ResultSetDynaClass(java.sql.ResultSet resultSet, boolean lowerCase) <br />
    <br />
    </font>如果 lowerCase 设为 false , 返回的数据栏位名将根据 JDBC 返回的为准. default 为 true.&nbsp;<code><br />
    <br />
    Connection conn = ...;<br />
    Statement stmt = conn.createStatement();<br />
    ResultSet rs = stmt.executeQuery("select account_id, name from customers");<br />
    Iterator rows = (new ResultSetDynaClass(rs)).iterator();<br />
    while (rows.hasNext()) {<br />
    &nbsp;&nbsp;&nbsp;&nbsp;DynaBean row = (DynaBean) rows.next();<br />
    &nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Account number is " + row.get("account_id") + " and name is " + row.get("name"));<br />
    }<br />
    rs.close();<br />
    stmt.close();<br />
    <br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code>
    <li><strong>RowSetDynaClass (Disconnected ResultSet as DynaBeans)</strong> - 使用 RowSet 的 Dynamic JavaBean<br />
    <br />
    <font color="#0000ff">RowSetDynaClass(java.sql.ResultSet resultSet)<br />
    RowSetDynaClass(java.sql.ResultSet resultSet, boolean lowerCase) <br />
    <br />
    </font>如果 lowerCase 设为 false , 返回的数据栏位名将根据 JDBC 返回的为准. default 为 true.&nbsp;<code><br />
    <br />
    Connection conn = ...;&nbsp;&nbsp;// Acquire connection from pool<br />
    Statement stmt = conn.createStatement();<br />
    ResultSet rs = stmt.executeQuery("SELECT ...");<br />
    RowSetDynaClass rsdc = new RowSetDynaClass(rs);<br />
    rs.close();<br />
    stmt.close();<br />
    ...;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Return connection to pool<br />
    List rows = rsdc.getRows();<br />
    ...;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Process the rows as desired&nbsp;&nbsp;<br />
    <br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code>
    <li><strong>WrapDynaBean and WrapDynaClass</strong> - 包装过的 Dynamic JavaBean<br />
    <br />
    如果你对于 DynaBean 的功能强大, 非常佩服的同时, 手边的 JavaBean 又不能随随便便就不用 那 你就把他包装起来 ....<br />
    <br />
    <font color="#0000ff">WrapDynaClass(java.lang.Class beanClass) <br />
    WrapDynaBean(java.lang.Object instance) <br />
    ConvertingWrapDynaBean(java.lang.Object instance) <br />
    <br />
    </font>
    <pre><code>MyBean bean = ...;<br />
    DynaBean wrapper = new WrapDynaBean(bean);<br />
    String firstName = wrapper.get("firstName"); &nbsp;</code></pre>
    </li>
</ul>
<p><font color="#ff0000"><strong>BeanUtils.ConvertUtils 介绍</strong></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在很多情况, 例如 struts framework 中, 就常常用到 request.getParameter 的参数, 需要转换成 正确的数据类型, 所以 ConvertUtils 就是来处理这些动作.<br />
<br />
<font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ConvertUtils().convert(java.lang.Object value)&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ConvertUtils().convert(java.lang.String[] values, java.lang.Class clazz)&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ConvertUtils().convert(java.lang.String value, java.lang.Class clazz)&nbsp;<br />
</font><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpServletRequest request = ...;<br />
&nbsp;&nbsp;&nbsp;&nbsp; MyBean bean = ...;<br />
&nbsp;&nbsp;&nbsp;&nbsp; HashMap map = new HashMap();<br />
&nbsp;&nbsp;&nbsp;&nbsp; Enumeration names = request.getParameterNames();<br />
&nbsp;&nbsp;&nbsp;&nbsp; while (names.hasMoreElements()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String name = (String) names.nextElement();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; map.put(name, request.getParameterValues(name));<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; BeanUtils.populate(bean, map);// it will use ConvertUtils for convertings<br />
<br />
&nbsp;</code></p>
&nbsp;&nbsp;&nbsp;&nbsp; 目前支持的类型有<br />
<br />
<ul>
    <li>sjava.lang.BigDecimal
    <li>java.lang.BigInteger
    <li>boolean and java.lang.Boolean
    <li>byte and java.lang.Byte
    <li>char and java.lang.Character
    <li>java.lang.Class
    <li>double and java.lang.Double
    <li>float and java.lang.Float
    <li>int and java.lang.Integer
    <li>long and java.lang.Long
    <li>short and java.lang.Short
    <li>java.lang.String
    <li>java.sql.Date
    <li>java.sql.Time
    <li>java.sql.Timestamp </li>
</ul>
&nbsp;&nbsp;&nbsp;&nbsp; 也可以建立自己的 converter&nbsp;<br />
<code><br />
&nbsp;&nbsp; Converter myConverter = new org.apache.commons.beanutils.converter.IntegerConverter();&nbsp;<br />
&nbsp;&nbsp; ConvertUtils.register(myConverter, Integer.TYPE);&nbsp;&nbsp;&nbsp;&nbsp;// Native type&nbsp;<br />
&nbsp;&nbsp; ConvertUtils.register(myConverter, Integer.class);&nbsp;&nbsp; // Wrapper class</code> 
<img src ="http://www.blogjava.net/zphab/aggbug/295632.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zphab/" target="_blank">张平辉</a> 2009-09-19 00:16 <a href="http://www.blogjava.net/zphab/articles/295632.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>静态化页面</title><link>http://www.blogjava.net/zphab/articles/295631.html</link><dc:creator>张平辉</dc:creator><author>张平辉</author><pubDate>Fri, 18 Sep 2009 16:06:00 GMT</pubDate><guid>http://www.blogjava.net/zphab/articles/295631.html</guid><wfw:comment>http://www.blogjava.net/zphab/comments/295631.html</wfw:comment><comments>http://www.blogjava.net/zphab/articles/295631.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zphab/comments/commentRss/295631.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zphab/services/trackbacks/295631.html</trackback:ping><description><![CDATA[<div>package com.zph.util;<br />
import java.io.ByteArrayOutputStream;<br />
import java.io.FileOutputStream;<br />
import java.io.IOException;<br />
import java.io.OutputStreamWriter;<br />
import java.io.PrintWriter;<br />
import java.nio.charset.Charset;<br />
import javax.servlet.RequestDispatcher;<br />
import javax.servlet.ServletContext;<br />
import javax.servlet.ServletException;<br />
import javax.servlet.ServletOutputStream;<br />
import javax.servlet.http.HttpServlet;<br />
import javax.servlet.http.HttpServletRequest;<br />
import javax.servlet.http.HttpServletResponse;<br />
import javax.servlet.http.HttpServletResponseWrapper;<br />
<br />
/**<br />
&nbsp;* @author shaevel<br />
&nbsp;* 该方法只能静态化本应用的动态页面<br />
&nbsp;* <br />
&nbsp;*/<br />
public class TestToHtml extends HttpServlet {<br />
&nbsp;private static final long serialVersionUID = -8637885029101603118L;<br />
&nbsp;public void doGet(HttpServletRequest request, HttpServletResponse response)<br />
&nbsp;&nbsp;&nbsp;throws IOException, ServletException {<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;Charset cs = Charset.forName("UTF-8");&nbsp; //设置生成文件的字符集<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;String url = "";<br />
&nbsp;&nbsp;String name = "";<br />
&nbsp;&nbsp;ServletContext sc = getServletContext();<br />
&nbsp;&nbsp;String file_name = request.getParameter("file_name");// 你要访问的jsp文件,如index.jsp<br />
&nbsp;&nbsp;// 则你访问这个servlet时加参数.如<a href="http://localhost/toHtml?file_name=index">http://localhost/toHtml?file_name=index</a><br />
&nbsp;&nbsp;url = "/" +&nbsp;file_name + ".jsp?id=zhang&amp;amp;m=13425109339";// 这是你要生成wml的jsp文件<br />
&nbsp;&nbsp;name = "E://1.wml";// 这是生成的wml文件??,如index.wml.<br />
&nbsp;&nbsp;RequestDispatcher rd = sc.getRequestDispatcher(url);<br />
&nbsp;&nbsp;final ByteArrayOutputStream os = new ByteArrayOutputStream();<br />
&nbsp;&nbsp;final ServletOutputStream stream = new ServletOutputStream() {<br />
&nbsp;&nbsp;&nbsp;public void write(byte[] data, int offset, int length) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;os.write(data, offset, length);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;public void write(int b) throws IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;os.write(b);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;};<br />
&nbsp;&nbsp;final PrintWriter pw = new PrintWriter(new OutputStreamWriter(os,cs));<br />
&nbsp;&nbsp;HttpServletResponse rep = new HttpServletResponseWrapper(response) {<br />
&nbsp;&nbsp;&nbsp;public ServletOutputStream getOutputStream() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return stream;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;public PrintWriter getWriter() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return pw;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;};<br />
&nbsp;&nbsp;rd.include(request, rep);<br />
&nbsp;&nbsp;pw.flush();<br />
&nbsp;&nbsp;FileOutputStream fos = new FileOutputStream(name); // 把jsp输出的内容写到xxx.wml<br />
&nbsp;&nbsp;os.writeTo(fos);<br />
&nbsp;&nbsp;fos.close();<br />
&nbsp;&nbsp;PrintWriter out = response.getWriter();<br />
&nbsp;&nbsp;out.print("&lt;p align=center&gt;&lt;font size=3 color=red&gt;successfully??&lt;/font&gt;&lt;/p&gt;");<br />
&nbsp;}<br />
&nbsp;public void doPost(HttpServletRequest request, HttpServletResponse response)<br />
&nbsp;&nbsp;&nbsp;throws IOException, ServletException {<br />
&nbsp;&nbsp;doGet(request, response);<br />
&nbsp;}<br />
}</div>
<img src ="http://www.blogjava.net/zphab/aggbug/295631.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zphab/" target="_blank">张平辉</a> 2009-09-19 00:06 <a href="http://www.blogjava.net/zphab/articles/295631.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Map Cache</title><link>http://www.blogjava.net/zphab/articles/295630.html</link><dc:creator>张平辉</dc:creator><author>张平辉</author><pubDate>Fri, 18 Sep 2009 16:04:00 GMT</pubDate><guid>http://www.blogjava.net/zphab/articles/295630.html</guid><wfw:comment>http://www.blogjava.net/zphab/comments/295630.html</wfw:comment><comments>http://www.blogjava.net/zphab/articles/295630.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zphab/comments/commentRss/295630.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zphab/services/trackbacks/295630.html</trackback:ping><description><![CDATA[<div>package com.zph.util;<br />
import java.util.HashMap;<br />
import java.util.Map;<br />
/**<br />
&nbsp;* @author shaevel<br />
&nbsp;*<br />
&nbsp;*/<br />
public class MapUtil {<br />
<br />
&nbsp;public static Map&lt;String, Object&gt; Map_Info = new HashMap&lt;String,Object&gt;();<br />
&nbsp;<br />
&nbsp;public static void addObject(String id, Object o){<br />
&nbsp;&nbsp;Map_Info.put(id, o);<br />
&nbsp;&nbsp;System.out.println("============== size: " + Map_Info.size() + " ================");<br />
&nbsp;}<br />
&nbsp;<br />
&nbsp;public static void moveObject(String id){<br />
&nbsp;&nbsp;if(Map_Info.get(id) != null){<br />
&nbsp;&nbsp;&nbsp;Map_Info.remove(id);<br />
&nbsp;&nbsp;}<br />
&nbsp;}<br />
&nbsp;<br />
&nbsp;public static Object getObject(String id){<br />
&nbsp;&nbsp;if(Map_Info.get(id) != null){<br />
&nbsp;&nbsp;&nbsp;return Map_Info.get(id);<br />
&nbsp;&nbsp;}else{<br />
&nbsp;&nbsp;&nbsp;return "";<br />
&nbsp;&nbsp;}<br />
&nbsp;}<br />
}<br />
</div>
<img src ="http://www.blogjava.net/zphab/aggbug/295630.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zphab/" target="_blank">张平辉</a> 2009-09-19 00:04 <a href="http://www.blogjava.net/zphab/articles/295630.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>读取Excel文件</title><link>http://www.blogjava.net/zphab/articles/295629.html</link><dc:creator>张平辉</dc:creator><author>张平辉</author><pubDate>Fri, 18 Sep 2009 16:02:00 GMT</pubDate><guid>http://www.blogjava.net/zphab/articles/295629.html</guid><wfw:comment>http://www.blogjava.net/zphab/comments/295629.html</wfw:comment><comments>http://www.blogjava.net/zphab/articles/295629.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zphab/comments/commentRss/295629.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zphab/services/trackbacks/295629.html</trackback:ping><description><![CDATA[<p>package com.zph.util;</p>
<p>import java.io.FileInputStream;<br />
import java.io.FileOutputStream;<br />
import org.apache.poi.hssf.usermodel.HSSFCell;<br />
import org.apache.poi.hssf.usermodel.HSSFRow;<br />
import org.apache.poi.hssf.usermodel.HSSFSheet;<br />
import org.apache.poi.hssf.usermodel.HSSFWorkbook;</p>
<p>/**<br />
&nbsp;* @author shaevel<br />
&nbsp;*<br />
&nbsp;*/<br />
public class ReadExcel {</p>
<p>&nbsp;//public static String outputFile="D:\\JTest\\gongye.xls";</p>
<p>&nbsp;/**<br />
&nbsp;* @param args<br />
&nbsp;*/<br />
&nbsp;public static void main(String[] args) {</p>
<p>&nbsp;&nbsp;String outputFile="D:\\gongye.xls";<br />
&nbsp;&nbsp;createXLS(outputFile);<br />
&nbsp;&nbsp;//String inputFile = "D:\\shuaka.xls";<br />
&nbsp;&nbsp;//readXLS(inputFile);<br />
&nbsp;}</p>
<p>&nbsp;public static void createXLS(String outputFile){</p>
<p>&nbsp;&nbsp;try{</p>
<p>&nbsp;&nbsp;&nbsp;// 创建新的Excel 工作簿<br />
&nbsp;&nbsp;&nbsp;HSSFWorkbook workbook = new HSSFWorkbook();</p>
<p>&nbsp;&nbsp;&nbsp;// 在Excel工作簿中建一工作表，其名为缺省值<br />
&nbsp;&nbsp;&nbsp;// 如要新建一名为"效益指标"的工作表，其语句为：<br />
&nbsp;&nbsp;&nbsp;// HSSFSheet sheet = workbook.createSheet("效益指标");<br />
&nbsp;&nbsp;&nbsp;HSSFSheet sheet = workbook.createSheet();</p>
<p>&nbsp;&nbsp;&nbsp;// 在索引0的位置创建行（最顶端的行）<br />
&nbsp;&nbsp;&nbsp;HSSFRow row = sheet.createRow((short)0);</p>
<p>&nbsp;&nbsp;&nbsp;//在索引0的位置创建单元格（左上端）<br />
&nbsp;&nbsp;&nbsp;HSSFCell cell = row.createCell((short) 0);</p>
<p>&nbsp;&nbsp;&nbsp;// 定义单元格为字符串类型<br />
&nbsp;&nbsp;&nbsp;cell.setCellType(HSSFCell.CELL_TYPE_STRING);<br />
&nbsp;&nbsp;&nbsp;cell.setEncoding(HSSFCell.ENCODING_UTF_16);</p>
<p>&nbsp;&nbsp;&nbsp;// 在单元格中输入一些内容<br />
&nbsp;&nbsp;&nbsp;cell.setCellValue("我们的故事");</p>
<p>&nbsp;&nbsp;&nbsp;// 新建一输出文件流<br />
&nbsp;&nbsp;&nbsp;FileOutputStream fOut = new FileOutputStream(outputFile);</p>
<p>&nbsp;&nbsp;&nbsp;// 把相应的Excel 工作簿存盘<br />
&nbsp;&nbsp;&nbsp;workbook.write(fOut);<br />
&nbsp;&nbsp;&nbsp;fOut.flush();</p>
<p>&nbsp;&nbsp;&nbsp;// 操作结束，关闭文件<br />
&nbsp;&nbsp;&nbsp;fOut.close();</p>
<p>&nbsp;&nbsp;&nbsp;System.out.println("文件生成...");<br />
&nbsp;&nbsp;}catch(Exception e) {<br />
&nbsp;&nbsp;&nbsp;System.out.println("已运行 xlCreate() : " + e );<br />
&nbsp;&nbsp;}<br />
&nbsp;}</p>
<p>&nbsp;public static void readXLS(String inputFile){</p>
<p>&nbsp;&nbsp;try{</p>
<p>&nbsp;&nbsp;&nbsp;// 创建对Excel工作簿文件的引用<br />
&nbsp;&nbsp;&nbsp;HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(inputFile));</p>
<p>&nbsp;&nbsp;&nbsp;// 创建对工作表的引用。<br />
&nbsp;&nbsp;&nbsp;// 本例是按名引用（让我们假定那张表有着缺省名"Sheet1"）<br />
&nbsp;&nbsp;&nbsp;HSSFSheet sheet = workbook.getSheet("Sheet1");</p>
<p>&nbsp;&nbsp;&nbsp;// 也可用getSheetAt(int index)按索引引用，<br />
&nbsp;&nbsp;&nbsp;// 在Excel文档中，第一张工作表的缺省索引是0，<br />
&nbsp;&nbsp;&nbsp;// 其语句为：HSSFSheet sheet = workbook.getSheetAt(0);<br />
&nbsp;&nbsp;&nbsp;// 读取左上端单元<br />
&nbsp;&nbsp;&nbsp;for(int i = 0; i &lt; 8; i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;for(int j =0; j &lt; 6; j++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HSSFRow row = sheet.getRow(i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HSSFCell cell = row.getCell((short)j);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 输出单元内容，cell.getStringCellValue()就是取所在单元的值<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(cell.getCellType() == 0){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.print(cell.getNumericCellValue() + "&nbsp; ");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(cell.getCellType() == 1){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.print(cell.getStringCellValue() + "&nbsp; ");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;System.out.print("\n");<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}catch(Exception e) {<br />
&nbsp;&nbsp;&nbsp;System.out.println("已运行xlRead() : " + e );<br />
&nbsp;&nbsp;}<br />
&nbsp;} <br />
}</p>
<img src ="http://www.blogjava.net/zphab/aggbug/295629.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zphab/" target="_blank">张平辉</a> 2009-09-19 00:02 <a href="http://www.blogjava.net/zphab/articles/295629.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate与JdbcTemplate共享事务管理</title><link>http://www.blogjava.net/zphab/articles/295627.html</link><dc:creator>张平辉</dc:creator><author>张平辉</author><pubDate>Fri, 18 Sep 2009 15:51:00 GMT</pubDate><guid>http://www.blogjava.net/zphab/articles/295627.html</guid><wfw:comment>http://www.blogjava.net/zphab/comments/295627.html</wfw:comment><comments>http://www.blogjava.net/zphab/articles/295627.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zphab/comments/commentRss/295627.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zphab/services/trackbacks/295627.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在Spring和Hibernate的配置文件中，我们可以对类中的方法进行事务控制，也就是说某个方法中含有多个数据库的写操作，我们可以通过创建一个Spring中的HibernateTransactionManager实例，把相应的sessionFactory注入到其的sessionFactory属性中，由事务声明的方式进行事务控制。样例如下：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="oaTM" class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="sessionFactory"&gt;&lt;ref bean="oaSessionFactory"/&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;但是这种方式有个局限性，如果该方法中既有调用Hibernate进行存储，也有通过JdbcTemplate进行数据库的写操作，如果在方法执行过程中，写数据发生异常时，只有纳入了事务管理的通过Hibernate进行存储的数据才会回滚，而通过JdbcTemplate方式进行操作的数据不会进行回滚。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;原因是Hibernate与JdbcTemplate使用的是不同DBConnection，而且JdbcTemplate未申明相应的事务管理，所以要想在既使用了Hibernate，也使用了JdbcTemplate进行数据库存储操作的方法进行事务管理，需要在Spring的配置文件做适当的修改，样例如下：&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="oaTM" class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="sessionFactory"&gt;&lt;ref bean="oaSessionFactory"/&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="dataSource"&gt;&lt;ref bean="oaDataSource"/&gt;&lt;/property&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JdbcTemplate和Hibernate能被wrap到同一个事务里。成立需要几点条件：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1、使用同一个datasource；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2、事务交由HibernateTransactionManager管理；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3、相关dao以及service需要使用runtime exception体系，使用spring提供的exception可以，自己封装设计的runtime exception体系也行。 </p>
<img src ="http://www.blogjava.net/zphab/aggbug/295627.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zphab/" target="_blank">张平辉</a> 2009-09-18 23:51 <a href="http://www.blogjava.net/zphab/articles/295627.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA生成文件</title><link>http://www.blogjava.net/zphab/articles/295623.html</link><dc:creator>张平辉</dc:creator><author>张平辉</author><pubDate>Fri, 18 Sep 2009 15:42:00 GMT</pubDate><guid>http://www.blogjava.net/zphab/articles/295623.html</guid><wfw:comment>http://www.blogjava.net/zphab/comments/295623.html</wfw:comment><comments>http://www.blogjava.net/zphab/articles/295623.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zphab/comments/commentRss/295623.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zphab/services/trackbacks/295623.html</trackback:ping><description><![CDATA[<p>package com.zph.test;</p>
<p>imp<wbr>ort java.io.File;<br />
imp<wbr>ort java.io.FileOutputStream;<br />
imp<wbr>ort java.io.OutputStreamWriter;</p>
<p>/**<br />
&nbsp;* @author pinghui.zhang<br />
&nbsp;*<br />
&nbsp;*/<br />
public class StringToFile {<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp; * 把字符串写入文本中<br />
&nbsp;&nbsp;&nbsp; * @param fileName&nbsp;生成的文件绝对路径<br />
&nbsp;&nbsp;&nbsp; * @param content&nbsp;文件要保存的内容<br />
&nbsp;&nbsp;&nbsp; * @param enc&nbsp;&nbsp;文件编码<br />
&nbsp;&nbsp;&nbsp; * @return<br />
&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public static boolean writeStringToFile(String fileName,String content,String enc) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; File file = new File(fileName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(file.isFile()){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; file.deleteOnExit();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; file = new File(file.getAbsolutePath());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OutputStreamWriter os = null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(enc==null||enc.length()==0){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; os = new OutputStreamWriter(new FileOutputStream(file));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; os = new OutputStreamWriter(new FileOutputStream(file),enc);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; os.write(content);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; os.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<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 true;<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; public static void main(String args[]){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int[] ids = {995, 996, 997};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer presb = new StringBuffer();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer sufsb = new StringBuffer();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; presb.append("&lt;%@ page language=\"java\" imp<wbr>ort=\"java.util.*\" pageEncoding=\"UTF-8\"%&gt;\n")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .append("&lt;%@ taglib uri=\"<a href="http://java.sun.com/jstl/core_rt">http://java.sun.com/jstl/core_rt\</a>" prefix=\"c\"%&gt;\n")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .append("&lt;%\n")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .append("\tString deptid = \"");<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sufsb.append("\";&nbsp;//只要修改此处即可\n")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .append("\trequest.setAttribute(\"dept\", com.dadi.oa.system.DepartmentUtil.getDepartmentInfo(new Long(deptid)));\n")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .append("\tString fromurl = request.getRequestURI().substring(request.getRequestURI().lastIndexOf(\"/\") + 1);\n")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .append("%&gt;\n")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .append("&lt;%@ include file=\"body.jsp\"%&gt;");<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String content = "";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String fileName = "";<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i = 0; i &lt; ids.length; i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fileName = "D:\\project1\\OA\\Co<wbr>de\\jsp\\anth\\" + ids[i] + ".jsp";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content = presb.toString() + ids[i] + sufsb.toString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writeStringToFile(fileName, content, "UTF-8");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>}<br />
</p>
<img src ="http://www.blogjava.net/zphab/aggbug/295623.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zphab/" target="_blank">张平辉</a> 2009-09-18 23:42 <a href="http://www.blogjava.net/zphab/articles/295623.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 Java 开源工具建立一个灵活的搜索引擎【转摘】</title><link>http://www.blogjava.net/zphab/articles/295622.html</link><dc:creator>张平辉</dc:creator><author>张平辉</author><pubDate>Fri, 18 Sep 2009 15:39:00 GMT</pubDate><guid>http://www.blogjava.net/zphab/articles/295622.html</guid><wfw:comment>http://www.blogjava.net/zphab/comments/295622.html</wfw:comment><comments>http://www.blogjava.net/zphab/articles/295622.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zphab/comments/commentRss/295622.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zphab/services/trackbacks/295622.html</trackback:ping><description><![CDATA[<p><a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#author" cmimpressionsent="1"><font color="#5c81a7">仇 寅</font></a> (<a href="mailto:qiuyin04@software.nju.edu.cn?subject=使用 Java 开源工具建立一个灵活的搜索引擎&amp;cc=zoucl@cn.ibm.com" cmimpressionsent="1"><font color="#5c81a7">qiuyin04@software.nju.edu.cn</font></a>)南京大学<br />
</p>
<p>2007 年 11 月 27 日</p>
<blockquote>为应用程序添加搜索能力经常是一个常见的需求。本文介绍了一个框架，开发者可以使用它以最小的付出实现搜索引擎功能，理想情况下只需要一个配置文件。该框架基于若干开源的库和工具，如 Apache Lucene，Spring 框架，cpdetector 等。它支持多种资源。其中两个典型的例子是数据库资源和文件系统资源。Indexer 对配置的资源进行索引并传输到中央服务器，之后这些索引可以通过 API 进行搜索。Spring 风格的配置文件允许清晰灵活的自定义和调整。核心 API 也提供了可扩展的接口。 </blockquote>
<p><a><span><strong><font size="5">引言</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为应用程序添加搜索能力经常是一个常见的需求。尽管已经有若干程序库提供了对搜索基础设施的支持，然而对于很多人而言，使用它们从头开始建立一个搜索引擎将是一个付出不小而且可能乏味的过程。另一方面，很多的小型应用对于搜索功能的需求和应用场景具有很大的相似性。本文试图以对多数小型应用的适用性为出发点，用 Java 语言构建一个灵活的搜索引擎框架。使用这个框架，多数情形下可以以最小的付出建立起一个搜索引擎。最理想的情况下，甚至只需要一个配置文件。特殊的情形下，可以通过灵活地对框架进行扩展满足需求。当然，如题所述，这都是借助开源工具的力量。 </p>
<p><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
</p>
<p><a><span><strong><font size="5">基础知识</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Apache Lucene 是开发搜索类应用程序时最常用的 Java 类库，我们的框架也将基于它。为了下文更好的描述，我们需要先了解一些有关 Lucene 和搜索的基础知识。注意，本文不关注索引的文件格式、分词技术等话题。 </p>
<p><a><span><strong><font size="3">什么是搜索和索引</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从用户的角度来看，搜索的过程是通过关键字在某种资源中寻找特定的内容的过程。而从计算机的角度来看，实现这个过程可以有两种办法。一是对所有资源逐个与关键字匹配，返回所有满足匹配的内容；二是如同字典一样事先建立一个对应表，把关键字与资源的内容对应起来，搜索时直接查找这个表即可。显而易见，第二个办法效率要高得多。建立这个对应表事实上就是建立逆向索引（inverted index）的过程。 </p>
<p><a><span><strong><font size="3">Lucene 基本概念</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Lucene 是 Doug Cutting 用 Java 开发的用于全文搜索的工具库。在这里，我假设读者对其已有基本的了解，我们只对一些重要的概念简要介绍。要深入了解可以参考 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#resources" cmimpressionsent="1"><font color="#5c81a7">参考资源</font></a> 中列出的相关文章和图书。下面这些是 Lucene 里比较重要的类。 </p>
<ul>
    <li><co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt;：索引包含多个 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt;。而每个 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 则包含多个 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 对象。<co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 可以是从数据库表里取出的一堆数据，可以是一个文件，也可以是一个网页等。注意，它不等同于文件系统中的文件。
    <li><co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt;：一个 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 有一个名称，它对应 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt;的一部分数据，表示文档的内容或者文档的元数据（与下文中提到的资源元数据不是一个概念）。一个 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 对象有两个重要属性：Store ( 可以有 YES, NO, COMPACT 三种取值 ) 和 Index ( 可以有 TOKENIZED, UN_TOKENIZED, NO, NO_NORMS 四种取值 )
    <li><co<wbr>DE&gt;<font face="Courier" size="2">Query</font></co<wbr>DE&gt;：抽象了搜索时使用的语句。
    <li><co<wbr>DE&gt;<font face="Courier" size="2">IndexSearcher</font></co<wbr>DE&gt;：提供 <co<wbr>DE&gt;<font face="Courier" size="2">Query</font></co<wbr>DE&gt; 对象给它，它利用已有的索引进行搜索并返回搜索结果。
    <li><co<wbr>DE&gt;<font face="Courier" size="2">Hits</font></co<wbr>DE&gt;：一个容器，包含了指向<em>一部分</em>搜索结果的指针。 </li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用 Lucene 来进行编制索引的过程大致为：将输入的数据源统一为字符串或者文本流的形式，然后从数据源提取数据，创建合适的 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 添加到对应数据源的 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 对象之中。 <br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
</p>
<p><a><span><strong><font size="5">系统概览</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;要建立一个通用的框架，必须对不同情况的共性进行抽象。反映到设计需要注意两点。一是要提供扩展接口；二是要尽量降低模块之间的耦合程度。我们的框架很简单地分为两个模块：索引模块和搜索模块。索引模块在不同的机器上各自进行对资源的索引，并把索引文件（事实上，下面我们会说到，还有元数据）统一传输到同一个地方（可以是在远程服务器上，也可以是在本地）。搜索模块则利用这些从多个索引模块收集到的数据完成用户的搜索请求。</p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#overallArch" cmimpressionsent="1"><font color="#5c81a7">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;图 1</font></a> 展现了整体的框架。可以看到，两个模块之间相对是独立的，它们之间的关联不是通过代码，而是通过索引和元数据。在下文中，我们将会详细介绍如何基于开源工具设计和实现这两个模块。</p>
<p><br />
<a><strong>图 1. 系统架构图</strong></a><br />
<div align="center"><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" alt="图 1. 系统架构图 " src="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/arch.png" /></div>
<br />
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p>&nbsp;</p>
<p><a><span><strong><font size="5">建立索引</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;可以进行索引的对象有很多，如文件、网页、RSS Feed 等。在我们的框架中，我们定义可以进行索引的<strong>一类</strong>对象为<em>资源</em>。从实现细节上来说，从一个资源中可以提取出多个 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 对象。文件系统资源和数据库结果集资源都是资源的代表性例子。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;前面提到，从资源中收集到的索引被统一传送到同一个地方，以被搜索模块所用。显然除了索引之外，搜索模块需要对资源有更多的了解，如资源的名称、搜索该资源后搜索结果的呈现格式等。这些额外的附加信息称为资源的<em>元数据</em>。元数据和索引数据一同被收集起来，放置到某个特定的位置。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;简要地介绍过资源的概念之后，我们首先为其定义一个 <co<wbr>DE&gt;<font face="Courier" size="2">Resource</font></co<wbr>DE&gt; 接口。这个接口的声明如下。</p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;<a><strong>清单 1. Resource 接口</strong></a><br />
<table style="width: 951px; height: 183px" cellspacing="0" cellpadding="0" width="951" align="center" border="0">
    <tbody>
        <tr>
            <td>
            <pre>public interface Resource {
            // RequestProcessor 对象被动地从资源中提取 Document，并返回提取的数量
            public int extractDocuments(ResourceProcessor processor);
            // 添加的 DocumentListener 将在每一个 Document 对象被提取出时被调用
            public void addDocumentListener(DocumentListener l);
            // 返回资源的元数据
            public ResourceMetaData getMetaData();
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其中元数据包含的字段见下表。在下文中，我们还会对元数据的用途做更多的介绍。</p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a><strong>表 1. 资源元数据包含的字段</strong></a><br />
<table style="width: 879px; height: 106px" cellspacing="0" cellpadding="0" width="879" align="center" border="0">
    <tbody>
        <tr>
            <th scope="col">属性</th>
            <th scope="col">类型</th>
            <th scope="col">含义</th>
        </tr>
        <tr>
            <th scope="row"><a>resourceName</a> </th>
            <td>String</td>
            <td>资源的<strong>唯一名称</strong> </td>
        </tr>
        <tr>
            <th scope="row">resourceDescription</th>
            <td>String</td>
            <td>资源的介绍性文字</td>
        </tr>
        <tr>
            <th scope="row"><a>hitTextPattern</a> </th>
            <td>String</td>
            <td>当文档被搜索到时，这个 pattern 规定了结果显示的格式</td>
        </tr>
        <tr>
            <th scope="row"><a>searchableFields</a> </th>
            <td>String[]</td>
            <td>可以被搜索的字段名称</td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;而 <co<wbr>DE&gt;<font face="Courier" size="2">DocumentListener</font></co<wbr>DE&gt; 的代码如下。</p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;<a><strong>清单 2. DocumentListener 接口</strong></a><br />
<table style="width: 952px; height: 71px" cellspacing="0" cellpadding="0" width="952" align="center" border="0">
    <tbody>
        <tr>
            <td>
            <pre>public interface DocumentListener extends EventListener {
            public void documentExtracted(Document doc);
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了让索引模块能够知道所有需要被索引的资源，我们在这里使用 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#resources" cmimpressionsent="1"><font color="#5c81a7">Spring</font></a> 风格的 XML 文件配置索引模块中的所有组件，尤其是所有资源。您可以在 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#downloads" cmimpressionsent="1"><font color="#5c81a7">下载部分</font></a> 查看一个示例配置文件。</p>
<p>
<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
    <tbody>
        <tr>
            <td width="10"><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td>
            <table cellspacing="0" cellpadding="5" width="100%" border="1">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee"><a><strong>为什么选择使用 Spring 风格的配置文件？</strong></a><br />
                        <p>这主要有两个好处： </p>
                        <ul>
                            <li>仅依赖于 Spring Core 和 Spring Beans 便免去了定义配置机制和解析配置文件的负担；
                            <li>Spring 的 IoC 机制降低了框架的耦合性，并使扩展框架变得简单； </li>
                        </ul>
                        <p>&nbsp;</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;基于以上内容，我们可以大致描述出索引模块工作的过程：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<ol>
    <li>首先在 XML 配置的 bean 中找出所有 <co<wbr>DE&gt;<font face="Courier" size="2">Resource</font></co<wbr>DE&gt; 对象；
    <li>对每一个调用其 <co<wbr>DE&gt;<font face="Courier" size="2">extractDocuments()</font></co<wbr>DE&gt; 方法，这一步除了完成对资源的索引外，还会在每次提取出一个 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 对象之后，通知注册在该资源上的所有 <co<wbr>DE&gt;<font face="Courier" size="2">DocumentListener</font></co<wbr>DE&gt;；
    <li>接着处理资源的元数据（<co<wbr>DE&gt;<font face="Courier" size="2">getMetaData()</font></co<wbr>DE&gt; 的返回值）；
    <li>将缓存里的数据写入到本地磁盘或者传送给远程服务器； </li>
</ol>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在这个过程中，有两个地方值得注意。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第一，对资源可以注册 <co<wbr>DE&gt;<font face="Courier" size="2">DocumentListener</font></co<wbr>DE&gt; 使得我们可以在运行时刻对索引过程有更为动态的控制。举一个简单例子，对某个文章发布站点的文章进行索引时，一个很正常的要求便是发布时间更靠近当前时间的文章需要在搜索结果中排在靠前的位置。每篇文章显然对应一个 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 对象，在 Lucene 中我们可以通过设置 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 的 <co<wbr>DE&gt;<font face="Courier" size="2">boost</font></co<wbr>DE&gt; 值来对其进行加权。假设其中文章发布时间的 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 的名称为 <co<wbr>DE&gt;<font face="Courier" size="2">PUB_TIME</font></co<wbr>DE&gt;，那么我们可以为资源注册一个 <co<wbr>DE&gt;<font face="Courier" size="2">DocumentListener</font></co<wbr>DE&gt;，当它被通知时，则检测 <co<wbr>DE&gt;<font face="Courier" size="2">PUB_TIME</font></co<wbr>DE&gt; 的值，根据距离当前时间的远近进行加权。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第二点很显然，在这个过程中，<co<wbr>DE&gt;<font face="Courier" size="2">extractDocuments()</font></co<wbr>DE&gt; 方法的实现依不同类型的资源而各异。下面我们主要讨论两种类型的资源：文件系统资源和数据库结果集资源。这两个类都实现了上面的 <co<wbr>DE&gt;<font face="Courier" size="2">接口</font></co<wbr>DE&gt;。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;文件系统资源</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对文件系统资源的索引通常从一个基目录开始，递归处理每个需要进行索引的文件。该资源有一个字符串数组类型的 <co<wbr>DE&gt;<font face="Courier" size="2">excludedFiles</font></co<wbr>DE&gt; 属性，表示在处理文件时需要排除的文件绝对路径的正则表达式。在递归遍历文件系统树的同时，绝对路径匹配 <co<wbr>DE&gt;<font face="Courier" size="2">excludedFiles</font></co<wbr>DE&gt; 中任意一项的文件将不会被处理。这主要是考虑到一般我们只需要对一部分文件夹（比如排除可能存在的备份目录）中的一部分文件（如 doc, ppt 文件等）进行索引。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;除了所有文件共有的文件名、文件路径、文件大小和修改时间等 Field，不同类型的文件需要有不同的处理方法。为了保留灵活性，我们使用 Strategy 模式封装对不同类型文件的处理方式。为此我们抽象出一个 <co<wbr>DE&gt;<font face="Courier" size="2">DocumentBuilder</font></co<wbr>DE&gt; 的接口，该接口仅定义了一个方法如下：</p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;<a><strong>清单 3. DocumentBuilder 接口</strong></a><br />
<table style="width: 951px; height: 71px" cellspacing="0" cellpadding="0" width="951" align="center" border="0">
    <tbody>
        <tr>
            <td>
            <pre>public interface DocumentBuilder {
            Document buildDocument(InputStream is);
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<table style="width: 303px; height: 247px" cellspacing="0" cellpadding="0" width="303" align="right" border="0">
    <tbody>
        <tr>
            <td width="10"><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td>
            <table style="width: 227px; height: 224px" cellspacing="0" cellpadding="5" width="227" border="1">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee"><a><strong>什么是 Strategy 模式？</strong></a><br />
                        <p>根据 Design patterns: Elements of reusable object orientated software 一书：Strategy 模式&#8220;定义一系列的算法，把它们分别封装起来，并且使它们相互可以替换。这个模式使得算法可以独立于使用它的客户而变化。&#8221;</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;不同的 <co<wbr>DE&gt;<font face="Courier" size="2">DocumentBuilder</font></co<wbr>DE&gt;（Strategy） 用于从一个输入流中读取数据，处理不同类型的文件。对于常见的文件格式来说，都有合适的开源工具帮助进行解析。在下表中我们列举一些常见文件类型的解析办法。</p>
<p>
<table style="width: 680px; height: 202px" cellspacing="0" cellpadding="0" align="center" border="0">
    <tbody>
        <tr>
            <th>文件类型</th>
            <th>常用扩展名</th>
            <th>可以使用的解析办法</th>
        </tr>
        <tr>
            <td>纯文本文档</td>
            <td>txt</td>
            <td>无需类库解析</td>
        </tr>
        <tr>
            <td>RTF 文档</td>
            <td>rtf</td>
            <td>使用 <co<wbr>DE&gt;<font face="Courier" size="2">javax.swing.text.rtf.RTFEditorKit</font></co<wbr>DE&gt; 类</td>
        </tr>
        <tr>
            <td>Word 文档（非 OOXML 格式）</td>
            <td>doc</td>
            <td>Apache POI （可配合使用 POI Scratchpad）</td>
        </tr>
        <tr>
            <td>PowerPoint 演示文稿（非 OOXML 格式）</td>
            <td>xls</td>
            <td>Apache POI （可配合使用 POI Scratchpad）</td>
        </tr>
        <tr>
            <td>PDF 文档</td>
            <td>pdf</td>
            <td>PDFBox（可能中文支持欠佳）</td>
        </tr>
        <tr>
            <td>HTML 文档</td>
            <td>htm, html</td>
            <td>JTidy, Cobra</td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这里以 Word 文件为例，给出一个简单的参考实现。</p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;<a><strong>清单 4. 解析纯文本内容的实现</strong></a><br />
<table style="width: 941px; height: 327px" cellspacing="0" cellpadding="0" width="941" align="center" border="0">
    <tbody>
        <tr>
            <td>
            <pre>// WordDocument 是 Apache POI Scratchpad 中的一个类
            Document buildDocument(InputStream is) {
            String bodyText = null;
            try {
            WordDocument wordDoc = new WordDocument(is);
            StringWriter sw = new StringWriter();
            wordDoc.writeAllText(sw);
            sw.close();
            bodyText = sw.toString();
            } catch (Exception e) {
            throw new DocumentHandlerException("Cannot extract text from a Word document", e);
            }
            if ((bodyText != null) &amp;&amp; (bodyText.trim().length() &gt; 0)) {
            Document doc = new Document();
            doc.add(new Field("body", bodyText, Field.Store.YES, Field.Index.TOKENIZED));
            return doc;
            }
            return null;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;那么如何选择合适的 Strategy 来处理文件呢？UNIX 系统下的 file(1) 工具提供了从 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#resources" cmimpressionsent="1"><font color="#5c81a7">magicnumber</font></a> 获取文件类型的功能，我们可以使用 <co<wbr>DE&gt;<font face="Courier" size="2">Runtime.exec()</font></co<wbr>DE&gt; 方法调用这一命令。但这需要在有 file(1) 命令的情况下，而且并不能识别出所有文件类型。在一般的情况下我们可以简单地根据扩展名来使用合适的类处理文件。扩展名和类的映射关系写在 properties 文件中。当需要添加对新的文件类型的支持时，我们只需添加一个新的实现 <co<wbr>DE&gt;<font face="Courier" size="2">DocumentBuilder</font></co<wbr>DE&gt; 接口的类，并在映射文件中添加一个映射关系即可。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;数据库结果集资源</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;大多数应用使用数据库作为永久存储，对数据库查询结果集索引是一个常见需求。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;生成一个数据库结果集资源的实例需要先提供一个查询语句，然后执行查询，得到一个结果集。这个结果集中的内容便是我们需要进行索引的对象。<co<wbr>DE&gt;<font face="Courier" size="2">extractDocuments</font></co<wbr>DE&gt; 的实现便是为结果集中的每一行创建一个 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 对象。和文件系统资源不同的是，数据库资源需要放入 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 中的 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 一般都存在在查询结果集之中。比如一个简单的文章发布站点，对其后台数据库执行查询 <co<wbr>DE&gt;<font face="Courier" size="2">SELECT ID, TITLE, CONTENT FROM ARTICLE</font></co<wbr>DE&gt; 返回一个有三列的结果集。对结果集的每一行都会被提取出一个 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 对象，其中包含三个 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt;，分别对应这三列。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;然而不同 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 的类型是不同的。比如 <co<wbr>DE&gt;<font face="Courier" size="2">ID</font></co<wbr>DE&gt; 字段一般对应 <co<wbr>DE&gt;<font face="Courier" size="2">Store.YES</font></co<wbr>DE&gt; 和 <co<wbr>DE&gt;<font face="Courier" size="2">Index.NO</font></co<wbr>DE&gt; 的 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt;；而 <co<wbr>DE&gt;<font face="Courier" size="2">TITLE</font></co<wbr>DE&gt; 字段则一般对应 <co<wbr>DE&gt;<font face="Courier" size="2">Store.YES</font></co<wbr>DE&gt; 和 <co<wbr>DE&gt;<font face="Courier" size="2">Index.TOKENIZED</font></co<wbr>DE&gt; 的 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt;。为了解决这个问题，我们在数据库结果集资源的实现中提供一个类型为 <co<wbr>DE&gt;<font face="Courier" size="2">Properties</font></co<wbr>DE&gt; 的 <co<wbr>DE&gt;<font face="Courier" size="2">fieldTypeMappings</font></co<wbr>DE&gt; 属性，用于设置数据库字段所对应的 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 的类型。对于前面的情况来说，这个属性可能会被配置成类似这样的形式：</p>
<p>
<table style="width: 956px; height: 71px" cellspacing="0" cellpadding="0" width="956" align="center" border="0">
    <tbody>
        <tr>
            <td>
            <pre>ID = YES, NO
            TITLE = YES, TOKENIZED
            CONTENT = NO, TOKENIZED</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;配合这个映射，我们便可以生成合适类型的 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt;，完成对结果集索引的工作。</p>
<p><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
</p>
<p><a><span><strong><font size="5">&nbsp;&nbsp;&nbsp;&nbsp;收集索引</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;完成对资源的索引之后，还需要让索引为搜索模块所用。前面我们已经说过这里介绍的框架主要用于小型应用，考虑到复杂性，我们采取简单地将分布在各个机器上的索引汇总到一个地方的策略。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;汇总索引的传输方式可以有很多方案，比如使用 FTP、HTTP、rsync 等。甚至索引模块和搜索模块可以位于同一台机器上，这种情况下只需要将索引进行本地拷贝即可。同前面类似，我们定义一个 <co<wbr>DE&gt;<font face="Courier" size="2">Transporter</font></co<wbr>DE&gt; 接口。</p>
<p><br />
&nbsp;&nbsp;&nbsp;&nbsp;<a><strong>清单 5. Transporter 接口</strong></a><br />
<table style="width: 956px; height: 71px" cellspacing="0" cellpadding="0" width="956" align="center" border="0">
    <tbody>
        <tr>
            <td>
            <pre>public interface Transporter {
            public void transport(File file);
            }</pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以 FTP 方式传输为例，我们使用 Commons Net 完成传输的操作。</p>
<p>
<table style="width: 954px; height: 359px" cellspacing="0" cellpadding="0" width="954" align="center" border="0">
    <tbody>
        <tr>
            <td>
            <pre>public void transport(File file) throws TransportException {
            FTPClient client = new FTPClient();
            client.connect(host);
            client.login(username, password);
            client.changeWorkingDirectory(remotePath);
            transportRecursive(client, file);
            client.disconnect();
            }
            public void transportRecursive(FTPClient client, File file) {
            if (file.isFile() &amp;&amp; file.canRead()) {
            client.storeFile(file.getName(), new FileInputStream(file));
            } else if (file.isDirectory()) {
            client.makeDirectory(file.getName());
            client.changeWorkingDirectory(file.getName());
            File[] fileList = file.listFiles();
            for (File f : fileList) {
            transportRecursive(client, f);
            }
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对其他传输方案也有各自的方案进行处理，具体使用哪个 <co<wbr>DE&gt;<font face="Courier" size="2">Transporter</font></co<wbr>DE&gt; 的实现被配置在 Spring 风格的索引模块配置文件中。传输的方式是灵活的。比如当需要强调安全性时，我们可以换用基于 SSL 的 FTP 进行传输。所需要做的只是开发一个使用 FTP over SSL 的 <co<wbr>DE&gt;<font face="Courier" size="2">Transporter</font></co<wbr>DE&gt; 实现，并在配置文件中更改 <co<wbr>DE&gt;<font face="Courier" size="2">Transporter</font></co<wbr>DE&gt; 的实现即可。</p>
<p><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
</p>
<p><a><span><strong><font size="5">&nbsp;&nbsp;&nbsp;&nbsp;进行搜索</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在做了这么多之后，我们开始接触和用户关联最为紧密的搜索模块。注意，我们的框架不包括一个搜索的 Web 前端界面。但是类似这样的界面可以在搜索模块的基础上方便地开发出来。基于已经收集好的索引进行搜索是个很简单的过程。Lucene 已经提供了功能强大的 <co<wbr>DE&gt;<font face="Courier" size="2">IndexSearcher</font></co<wbr>DE&gt; 及其子类。在这个部分，我们不会再介绍如何使用这些类，而是关注在前文提到过的资源元数据上。元数据从各个资源所在的文件夹中读取得到，它在搜索模块中扮演重要的角色。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;构建一个查询</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对不同资源进行搜索的查询方法并不一样。例如搜索一个论坛里的所有留言时，我们关注的一般是留言的标题、作者和内容；而当搜索一个 FTP 站点时，我们更多关注的是文件名和文件内容。另一方面，我们有时可能会使用一个查询去搜索多个资源的结果。这正是之前我们在前面所提到的元数据中 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#searchableFields" cmimpressionsent="1"><font color="#5c81a7"><co<wbr>DE&gt;<font face="Courier" size="2">searchableFields</font></co<wbr>DE&gt; </font></a>和 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#resourceName" cmimpressionsent="1"><font color="#5c81a7"><co<wbr>DE&gt;<font face="Courier" size="2">resourceName</font></co<wbr>DE&gt; </font></a>属性的作用。前者指出一个资源中哪些字段是参与搜索的；后者则用于在搜索时确定使用哪个或者哪些索引。从技术细节来说，只有有了这些信息，我们才可以构造出可用的 <co<wbr>DE&gt;<font face="Courier" size="2">Query</font></co<wbr>DE&gt; 对象。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;呈现搜索结果</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当从 <co<wbr>DE&gt;<font face="Courier" size="2">IndexSearcher</font></co<wbr>DE&gt; 对象得到搜索结果（<co<wbr>DE&gt;<font face="Courier" size="2">Hits</font></co<wbr>DE&gt;）之后，当然我们可以直接从中获取需要的值，再格式化予以输出。但一来格式化输出搜索结果（尤其在 Web 应用中）是个很常见的需求，可能会经常变更；二来结果的呈现格式应该是由分散的资源各自定义，而不是交由搜索模块来定义。基于上面两个原因，我们的框架将使用在资源收集端配置结果输出格式的方式。这个格式由资源元数据中的 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#hitTextPattern" cmimpressionsent="1"><font color="#5c81a7"><co<wbr>DE&gt;<font face="Courier" size="2">hitTextPattern</font></co<wbr>DE&gt; </font></a>属性定义。该属性是一个字符串类型的值，支持两种语法 </p>
<ul>
    <li>形如 <co<wbr>DE&gt;<font face="Courier" size="2">${field_name}</font></co<wbr>DE&gt; 的子字符串都会被动态替换成查询结果中各个 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 内 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 的值。
    <li>形如 <co<wbr>DE&gt;<font face="Courier" size="2">$function(...) </font></co<wbr>DE&gt;的被解释为函数，括号内以逗号隔开的符号都被解释成参数，函数可以嵌套。 </li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;例如搜索&#8220;具体&#8221;返回的搜索结果中包含一个 <co<wbr>DE&gt;<font face="Courier" size="2">Document</font></co<wbr>DE&gt; 对象，其 <co<wbr>DE&gt;<font face="Courier" size="2">Field</font></co<wbr>DE&gt; 如下表： </p>
<p>&nbsp;</p>
<p>
<table style="width: 955px; height: 86px" cellspacing="0" cellpadding="0" width="955" align="center" border="0">
    <tbody>
        <tr>
            <th>Field 名称</th>
            <th>Field 内容</th>
        </tr>
        <tr>
            <td>url</td>
            <td>http://example.org/article/1.html</td>
        </tr>
        <tr>
            <td>title</td>
            <td>示例标题</td>
        </tr>
        <tr>
            <td>content</td>
            <td>这里是具体的内容。</td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;那么如果 <co<wbr>DE&gt;<font face="Courier" size="2">hitTextPatten</font></co<wbr>DE&gt; 被设置为&#8220;<co<wbr>DE&gt;<font face="Courier" size="2">&lt;a href="${url}"&gt;${title}&lt;/a&gt;&lt;br/&gt;$highlight(${content}, 5, "&lt;b&gt;", "&lt;/b&gt;")</font></co<wbr>DE&gt;&#8221;，返回的结果经浏览器解释后可能的显示结果如下（这只是个演示链接，请不要点击）：</p>
<p><a href="http://example.org/article/1.html" cmimpressionsent="1"><font color="#5c81a7">&nbsp;&nbsp;&nbsp;&nbsp;示例标题</font></a>&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;这里是<strong>具体</strong>...</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上面提到的 <co<wbr>DE&gt;<font face="Courier" size="2">$highlight()</font></co<wbr>DE&gt; 函数用于在搜索结果中取得最匹配的一段文本，并高亮显示搜索时使用的短语，其第一个参数是高亮显示的文本，第二个参数是显示的文本长度，第三和第四个参数是高亮文本时使用的前缀和后缀。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;可以使用正则表达式和文本解析来实现前面所提到的语法。我们也可以使用 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#resources" cmimpressionsent="1"><font color="#5c81a7">JavaCC</font></a> 定义 <co<wbr>DE&gt;<font face="Courier" size="2">hitTextPattern</font></co<wbr>DE&gt; 的文法，进而生成词法分析器和语法解析器。这是更为系统并且相对而言不易出错的方法。对 JavaCC 的介绍不是本文的重点，您可以在下面的 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#resources" cmimpressionsent="1"><font color="#5c81a7">阅读资源</font></a> 中找到学习资料。</p>
<p><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
</p>
<p><a><span><strong><font size="5">&nbsp;&nbsp;&nbsp;&nbsp;相关产品</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面列出的是一些与我们所提出的框架所相关或者类似的产品，您可以在 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#resources" cmimpressionsent="1"><font color="#5c81a7">学习资料</font></a> 中更多地了解他们。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;IBM&#174;OmniFind?Family</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OmniFind 是 IBM 公司推出的企业级搜索解决方案。基于 UIMA (Unstructured Information Management Architecture) 技术，它提供了强大的索引和获取信息功能，支持巨大数量、多种类型的文档资源（无论是结构化还是非结构化），并为 Lotus&#174;Domino&#174;和 WebSphere&#174;Portal 专门进行了优化。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;Apache Solr</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Solr 是 Apache 的一个企业级的全文检索项目，实现了一个基于 HTTP 的搜索服务器，支持多种资源和 Web 界面管理，它同样建立在 Lucene 之上，并对 Lucene 做了很多扩展，例如支持动态字段及唯一键，对查询结果进行动态分组和过滤等。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;Google SiteSearch</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用 Google 的站点搜索功能可以方便而快捷地建立一个站内搜索引擎。但是 Google 的站点搜索基于 Google 的网络爬虫，所以无法访问受保护的站点内容或者 Intranet 上的资源。另外，Google 所支持的资源类型也是有限的，我们无法对其进行扩展。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;SearchBlox? </font></strong></span></a></p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#resources" cmimpressionsent="1"><font color="#5c81a7">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SearchBlox</font></a> 是一个商业的搜索引擎构建框架。它本身是一个 J2EE 组件，和我们的框架类似，也支持对网页和文件系统等资源进行索引，进而进行搜索。</p>
<p><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
</p>
<p><a><span><strong><font size="5">&nbsp;&nbsp;&nbsp;&nbsp;还需考虑的问题</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;本文介绍的思想试图利用开源的工具解决中小型应用中的常见问题。当然，作为一个框架，它还有很多不足，下面列举出一些可以进行改进的地方。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;性能考虑</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当需要进行索引的资源数目不多时，隔一定的时间进行一次完全索引不会占用很长时间。使用一台 2G 内存，Xeon 2.66G 处理器的服务器进行实际测试，发现对数据库资源的索引占用的时间很少，一千多条记录花费的时间在 1 秒到 2 秒之内。而对 1400 多个文件进行索引耗时大约十几秒。但在大型应用中，资源的容量是巨大的，如果每次都进行完整的索引，耗费的时间会很惊人。我们可以通过跳过已经索引的资源内容，删除已不存在的资源内容的索引，并进行增量索引来解决这个问题。这可能会涉及文件校验和索引删除等。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;另一方面，框架可以提供查询缓存来提高查询效率。框架可以在内存中建立一级缓存，并使用如 <a href="http://www.opensymphony.com/oscache/" cmimpressionsent="1"><font color="#5c81a7">OSCache</font></a> 或 <a href="http://ehcache.sourceforge.net/" cmimpressionsent="1"><font color="#5c81a7">EHCache</font></a> 实现磁盘上的二级缓存。当索引的内容变化不频繁时，使用查询缓存更会明显地提高查询速度、降低资源消耗。</p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;分布式索引</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们的框架可以将索引分布在多台机器上。搜索资源时，查询被 flood 到各个机器上从而获得搜索结果。这样可以免去传输索引到某一台中央服务器的过程。当然也可以基于实现了分布式哈希表 （DHT）的结构化 P2P 网络，配合索引复制 (Replication)，使得应用程序更为安全，可靠，有伸缩性。在 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-sefrmk/#resources" cmimpressionsent="1"><font color="#5c81a7">阅读资料</font></a> 中给出了 一篇关于构建分布式环境下全文搜索的可行性的论文。 </p>
<p><a><span><strong><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;安全性</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;目前我们的框架并没有涉及到安全性。除了依赖资源本身的访问控制（如受保护的网页和文件系统等）之外，我们还可以从两方面增强框架本身的安全性： </p>
<ol>
    <li>考虑到一个组织的搜索功能对不同用户的权限设置不一定一样，可以支持对用户角色的定义，实行对搜索模块的访问控制。
    <li>在资源索引模块中实现一种机制，让资源可以限制自己暴露的内容，从而缩小索引模块的索引范围。这可以类比 robots 文件可以规定搜索引擎爬虫的行为。 </li>
</ol>
<p>&nbsp;</p>
<p><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img title="使用 Java 开源工具建立一个灵活的搜索引擎【转摘】 - 大漠落日 - 澹泊明志 宁静致远 从容淡定 宠辱不惊" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
</p>
<p><a><span><strong><font size="5">&nbsp;&nbsp;&nbsp;&nbsp;总结</font></strong></span></a></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过上文的介绍，我们认识了一个可扩展的框架，由索引模块和搜索模块两部分组成。它可以灵活地适应不同的应用场景。如果需要更独特的需求，框架本身预留了可以扩展的接口，我们可以通过实现这些接口完成功能的定制。更重要的是这一切都是建立在开源软件的基础之上。希望本文能为您揭示开源的力量，体验用开源工具组装您自己的解决方案所带来的莫大快乐。</p>
<img src ="http://www.blogjava.net/zphab/aggbug/295622.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zphab/" target="_blank">张平辉</a> 2009-09-18 23:39 <a href="http://www.blogjava.net/zphab/articles/295622.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>让GC扫面持久区permanent generation【转摘】</title><link>http://www.blogjava.net/zphab/articles/295619.html</link><dc:creator>张平辉</dc:creator><author>张平辉</author><pubDate>Fri, 18 Sep 2009 15:24:00 GMT</pubDate><guid>http://www.blogjava.net/zphab/articles/295619.html</guid><wfw:comment>http://www.blogjava.net/zphab/comments/295619.html</wfw:comment><comments>http://www.blogjava.net/zphab/articles/295619.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zphab/comments/commentRss/295619.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zphab/services/trackbacks/295619.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一般GC算法也是会照顾permanent generation的，每次permanent generation满了要做扩展前都会触发一次FULL GC，除非设置了-Xnoclassgc。&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 另外如果使用CMS（ConcMarkSweep GC）算法的话，开了-XX:+UseConcMarkSweepGC标志，默认情况下就是不会扫描permanent generation的，需要同时打开下面两个标志位才能让CMS GC扫描permanent generation。&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-XX:+CMSPermGenSweepingEnabled&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-XX:+CMSClassUnloadingEnabled&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PS：只针对SUN的JVM有效。 
<img src ="http://www.blogjava.net/zphab/aggbug/295619.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zphab/" target="_blank">张平辉</a> 2009-09-18 23:24 <a href="http://www.blogjava.net/zphab/articles/295619.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>