<?xml version="1.0" encoding="gb2312"?>
<rss version="2.0">
<channel>
<title><![CDATA[终南的IT空间]]></title>
        <image>
        <title>http://hi.baidu.com</title>
        <link>http://hi.baidu.com</link>
        <url>http://img.baidu.com/img/logo-hi.gif</url>
        </image>
<description><![CDATA[更多原创技术文章，多样程序人生]]></description>
<link>http://hi.baidu.com/li%5Fzhongnan</link>
<language>zh-cn</language>
<generator>www.baidu.com</generator>
<ttl>5</ttl>


<item>
        <title><![CDATA[多线程中的死锁举例与分析]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/57c20fde986aba52cdbf1ada.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">多线程中的死锁举例与分析</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p><strong>1. 一个特殊构造的程序</strong></p>
<p>考虑下面这个专门为说明多线程中的死锁现象而构造的程序：</p>
<p>import java.util.LinkedList;</p>
<p>public class Stack {<br>
public static void main(String[] args) {<br>
&nbsp;&nbsp;  final Stack stack = new Stack();<br>
&nbsp;&nbsp;  new Thread(&quot;push&quot;) {<br>
&nbsp;&nbsp;&nbsp;  @Override<br>
&nbsp;&nbsp;&nbsp;  public void run() {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  for(int i = 0; i &lt; 100; i++)<br>
&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  try {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  Thread.sleep(10);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  } catch (InterruptedException e) {}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  stack.push(&quot;object &quot; + i);<br>
&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;  }&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;  }.start();<br>
&nbsp;&nbsp;  new Thread(&quot;pop&quot;) {<br>
&nbsp;&nbsp;&nbsp;  @Override<br>
&nbsp;&nbsp;&nbsp;  public void run() {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  for(int i = 0; i &lt; 100; i++)<br>
&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  try {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  System.out.println(stack.pop());<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  } catch (Exception e) {}<br>
&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;  }&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;  }.start();<br>
}</p>
<p>LinkedList&lt;Object&gt; list = new LinkedList&lt;Object&gt;();<br>
public synchronized void push(Object x) {<br>
&nbsp;&nbsp;  System.out.println(&quot;begin to push &quot; + x);<br>
&nbsp;&nbsp;  synchronized (list) {<br>
&nbsp;&nbsp;&nbsp;  list.addLast(x);<br>
&nbsp;&nbsp;&nbsp;  notify();<br>
&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;  System.out.println(&quot;end to push &quot; + x);<br>
}<br>
public synchronized Object pop() throws Exception {<br>
&nbsp;&nbsp;  System.out.println(&quot;begin to pop&quot;);<br>
&nbsp;&nbsp;  synchronized (list) {<br>
&nbsp;&nbsp;&nbsp;  if (list.size() &lt;= 0) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  wait();<br>
&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;  return list.removeLast();<br>
&nbsp;&nbsp;  }<br>
}<br>
}</p>
<p>该程序构造了一个 Stack，启动了两个线程。一个线程向 Stack 中添加数据，另外一个线程从 Stack 中取出数据并打印。但是运行程序后就会发现程序输出：</p>
<p>begin to pop<br>
begin to push object 0</p>
<p>后，在再也没有后续输出了。</p>
<p><strong>2. Dump 并分析线程状态</strong></p>
<p>启动 jvisualvm 查看该程序线程的状态，将其 Dump，就可以得到以下结果：</p>
<p>&quot;pop&quot; prio=6 tid=0x02b11800 nid=0x3648 in Object.wait() [0x02eaf000..0x02eafd14]<br>
&nbsp;&nbsp;  java.lang.Thread.State: WAITING (on object monitor)<br>
at java.lang.Object.wait(Native Method)<br>
- waiting on &lt;0x22ee0000&gt; (a Stack)<br>
at java.lang.Object.wait(Object.java:485)<br>
at Stack.pop(Stack.java:44)<br>
- locked &lt;0x22ee0010&gt; (a java.util.LinkedList)<br>
- locked &lt;0x22ee0000&gt; (a Stack)<br>
at Stack$2.run(Stack.java:24)</p>
<p>&nbsp;&nbsp;  Locked ownable synchronizers:<br>
- None</p>
<p>&quot;push&quot; prio=6 tid=0x02b28800 nid=0x4120 waiting for monitor entry [0x02e5f000..0x02e5fd94]<br>
&nbsp;&nbsp;  java.lang.Thread.State: BLOCKED (on object monitor)<br>
at Stack.push(Stack.java:35)<br>
- waiting to lock &lt;0x22ee0010&gt; (a java.util.LinkedList)<br>
- locked &lt;0x22ee0000&gt; (a Stack)<br>
at Stack$1.run(Stack.java:14)</p>
<p>&nbsp;&nbsp;  Locked ownable synchronizers:<br>
- None</p>
<p>可以看到，pop 线程正在运行 wait(); 语句，处于 WAITING 状态，同时，该线程锁住了 list 和 stack 对象。push 线程处于 BLOCKED 状态，等待其他线程释放 list 对象。</p>
<p><strong>3. 运行过程及死锁原因分析</strong><span><font face="Times New Roman"> </font></span></p>
<p class="MsoNormal"><span>
<table class="MsoTableGrid8" style="border-right: medium none; border-top: medium none; border-left: medium none; border-bottom: medium none; border-collapse: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td>
            <p style="text-align: center"><strong><span style="color: white; "><font color="#000000"> 步骤 </font></span></strong></p>
            </td>
            <td>
            <p style="text-align: center"><strong><span style="color: white; "><font color="#000000">主程序</font></span></strong></p>
            </td>
            <td>
            <p align="center"><strong>pop 线程</strong></p>
            </td>
            <td>
            <p style="text-align: center"><strong><span><font color="#000000">push 线程</font></span></strong></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>1</span></strong></p>
            </td>
            <td>
            <p class="MsoNormal"><span>启动</span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>2</span></strong></p>
            </td>
            <td>
            <p class="MsoNormal"><span>创建</span><span> stack </span><span>对象</span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>3</span></strong></p>
            </td>
            <td>
            <p class="MsoNormal"><span>创建</span> <span>list </span><span>对象</span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>4</span></strong></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
            <td>
            <p class="MsoNormal"><span>启动</span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>5</span></strong></p>
            </td>
            <td> </td>
            <td> </td>
            <td>
            <p class="MsoNormal"><span>启动</span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>6</span></strong></p>
            </td>
            <td> </td>
            <td> </td>
            <td>
            <p class="MsoNormal"><span>sleep 10ms</span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>7</span></strong></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
            <td>
            <p class="MsoNormal"><span>调用</span> <span>stack.pop()</span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>8</span></strong></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
            <td>
            <p class="MsoNormal"><span>锁住</span> <span>stack </span><span>对象</span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>9</span></strong></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
            <td>
            <p class="MsoNormal"><span>打印</span><span> &quot;begin to pop&quot;</span></p>
            </td>
            <td>
            <p class="MsoNormal"><span> </span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>10</span></strong></p>
            </td>
            <td> </td>
            <td>
            <p class="MsoNormal"><span>锁住</span> <span>list </span><span>对象</span></p>
            </td>
            <td> </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>11</span></strong></p>
            </td>
            <td> </td>
            <td>
            <p class="MsoNormal">调用 stack.wait()<br>
            （暂时释放 stack 对象）</p>
            </td>
            <td> </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>12</span></strong></p>
            </td>
            <td> </td>
            <td> </td>
            <td>
            <p class="MsoNormal"><span>锁住</span> <span>stack </span><span>对象</span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>13</span></strong></p>
            </td>
            <td> </td>
            <td> </td>
            <td>
            <p class="MsoNormal"><span>打印</span><span> &quot;begin to push 0&quot;</span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>14</span></strong></p>
            </td>
            <td> </td>
            <td> </td>
            <td>
            <p class="MsoNormal"><span>企图锁住</span> <span>list </span><span>对象<br>
            </span><span>(</span><span>发现</span><span> list </span><span>已被其他线程锁住</span><span>)</span></p>
            </td>
        </tr>
        <tr>
            <td>
            <p style="text-align: center"><strong><span>15</span></strong></p>
            </td>
            <td colspan="3">
            <p class="MsoNormal"><strong><span style="color: red; ">进入死锁状态</span></strong></p>
            </td>
        </tr>
    </tbody>
</table>
</span></p>
<p class="MsoNormal"><span> </span></p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/57c20fde986aba52cdbf1ada.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/Java">Java</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/57c20fde986aba52cdbf1ada.html#comment">查看评论</a>]]></description>
        <pubDate>2009年10月22日 星期四  14:29</pubDate>
        <category><![CDATA[Java]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/57c20fde986aba52cdbf1ada.html</guid>
</item>

<item>
        <title><![CDATA[Hessian 与 Session]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/37fe8ffaefe9ad17a9d31153.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">Hessian 与 Session</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p><strong>1. ServiceContext</strong></p>
<p>ServiceContext 代表为 Hessian 客户端提供服务的上下文环境，用来处理与客户端请求有关的信息。在最简单和常用的应用中，在服务器端可以通过 ServiceContext 来获取代表客户端的 ServletRequest (在 HTTP 环境中为 HttpServletRequest)，因此就可以知道客户端的相关信息，如客户端的 IP 地址、端口、Header和用户名等，重要的是可以获取到代表会话的 Session 对象。</p>
<p>代码举例：<br>
<font color="#0000ff"><em>package example;</em></font></p>
<p><font color="#0000ff"><em>import java.util.ArrayList;<br>
import java.util.Enumeration;<br>
import java.util.List;<br>
import javax.servlet.http.HttpServletRequest;<br>
import com.caucho.services.server.ServiceContext;</em></font></p>
<p><font color="#0000ff"><em>public class BasicService implements Basic {<br>
public String hello(String name) {<br>
&nbsp;&nbsp;  System.out.println(&quot;======== hello ========\n&quot;);<br>
&nbsp;&nbsp;  HttpServletRequest req = (HttpServletRequest) ServiceContext<br>
&nbsp;&nbsp;&nbsp;&nbsp;  .getContextRequest();<br>
&nbsp;&nbsp;  req.getSession().setAttribute(&quot;port&quot;, 1000);</em></font></p>
<p><font color="#0000ff"><em>&nbsp;&nbsp;  System.out.println(&quot;getRemoteAddr() = &quot; + req.getRemoteAddr());<br>
&nbsp;&nbsp;  System.out.println(&quot;getRemotePort() = &quot; + req.getRemotePort());<br>
&nbsp;&nbsp;  System.out.println(&quot;getRemoteUser() = &quot; + req.getRemoteUser());<br>
&nbsp;&nbsp;  System.out<br>
&nbsp;&nbsp;&nbsp;&nbsp;  .println(&quot;getSession().getId() = &quot; + req.getSession().getId());<br>
&nbsp;&nbsp;  for (Enumeration&lt;String&gt; e = req.getHeaderNames(); e.hasMoreElements();) {<br>
&nbsp;&nbsp;&nbsp;  String headerName = e.nextElement();<br>
&nbsp;&nbsp;&nbsp;  System.out.println(headerName + &quot; = &quot; + req.getHeader(headerName));</em></font></p>
<p><font color="#0000ff"><em>&nbsp;&nbsp;  }</em></font></p>
<p><font color="#3366ff"><font color="#0000ff"><em>&nbsp;&nbsp;  return &quot;Hello, &quot; + name;<br>
}<br>
}</em></font><br>
</font>在上面的示例中，通过 ServiceContext 的静态方法 getContextRequest() 获取代表客户端请求的 HttpServletRequest 对象，然后就可以像普通 Web 编程那样使用 Request 和 Session 对象了。</p>
<p><strong>2. Hessian 中 Sessian 的问题</strong></p>
<p>一般来说，RPC 或 Web Service 这类的应用都是无状态的，但是有些应用则需要有状态的远程调用，比如先登录认证，然后再执行其他操作。<br>
Hessian 本身也没有对有状态的应用提供直接支持。查看 HessianProxy 的代码可以发现，每次发送请求的时候，Hessain 都会调用 HessianProxyFactory 类的 openConnection 重新打开连接，并且在远程调用完成后关闭连接。</p>
<p>重新打开连接<br>
<em><font color="#0000ff">protected URLConnection sendRequest(String methodName, Object []args)<br>
&nbsp;&nbsp;&nbsp;  throws IOException<br>
{<br>
&nbsp;&nbsp;&nbsp;  URLConnection conn = null;<br>
&nbsp;&nbsp;&nbsp;  <br>
&nbsp;&nbsp;&nbsp;  conn = _factory.openConnection(_url);<br>
&nbsp;&nbsp;&nbsp;  boolean isValid = false;</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;  try {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  // Used chunked mode when available, i.e. JDK 1.5.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if (_factory.isChunkedPost() &amp;&amp; conn instanceof HttpURLConnection) {<br>
try {<br>
&nbsp;&nbsp;  HttpURLConnection httpConn = (HttpURLConnection) conn;</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;  httpConn.setChunkedStreamingMode(8 * 1024);<br>
} catch (Throwable e) {<br>
}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;  <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  addRequestHeaders(conn);</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  OutputStream os = null;</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  try {<br>
os = conn.getOutputStream();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  } catch (Exception e) {<br>
throw new HessianRuntimeException(e);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if (log.isLoggable(Level.FINEST)) {<br>
PrintWriter dbg = new PrintWriter(new LogWriter(log));<br>
os = new HessianDebugOutputStream(os, dbg);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  AbstractHessianOutput out = _factory.getHessianOutput(os);</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  out.call(methodName, args);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  out.flush();</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  isValid = true;</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return conn;<br>
&nbsp;&nbsp;&nbsp;  } finally {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if (! isValid &amp;&amp; conn instanceof HttpURLConnection)<br>
((HttpURLConnection) conn).disconnect();<br>
&nbsp;&nbsp;&nbsp;  }<br>
}</font></em></p>
<p>关闭连接<br>
<em><font color="#0000ff">public Object invoke(Object proxy, Method method, Object []args)<br>
&nbsp;&nbsp;&nbsp;  throws Throwable<br>
{<br>
...<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  conn = sendRequest(mangleName, args);</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if (conn instanceof HttpURLConnection) {<br>
httpConn = (HttpURLConnection) conn;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int code = 500;</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  try {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  code = httpConn.getResponseCode();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  } catch (Exception e) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  parseResponseHeaders(conn);<br>
&nbsp;&nbsp;&nbsp;  ...<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  try {<br>
if (httpConn != null)<br>
&nbsp;&nbsp;  httpConn.disconnect();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  } catch (Exception e) {<br>
log.log(Level.FINE, e.toString(), e);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;  }<br>
}</font></em></p>
<p>在登录认证的应用中，在调用登录认证的方法后执行其他业务操作时，需要对用户是否登录进行验证，由于 Hessian 每次调用都会重新打开和关闭连接，因此每次调用都是一个新的 Session，前面是否登录的信息就不能获取到。</p>
<p><strong>3. 扩展 Hessian 的客户端程序实现对 Sessian 的支持</strong></p>
<p>HTTP 是一个无状态的协议，其状态的实现是通过 Cookie 来实现的，根据 HTTP 的这个特点，我们可以在 Hessian 客户端首次调用后的每次调用中，将首次调用得到的 Cookie 添加到 HTTP Header 中，从而模拟出有状态的 HTTP 连接。<br>
通过查看 HessianProxy 类的代码发现，其中有两个没有实现内容方法，可以获取服务器端返回的头信息 和设置发送给服务器端的 HTTP 请求头信息：</p>
<p><em><font color="#0000ff">/**<br>
&nbsp;&nbsp;  * Method that allows subclasses to parse response headers such as cookies.<br>
&nbsp;&nbsp;  * Default implementation is empty. <br>
&nbsp;&nbsp;  * @param conn<br>
&nbsp;&nbsp;  */<br>
protected void parseResponseHeaders(URLConnection conn) {<br>
&nbsp;&nbsp;  <br>
}<br>
<br>
/**<br>
&nbsp;&nbsp;  * Method that allows subclasses to add request headers such as cookies.<br>
&nbsp;&nbsp;  * Default implementation is empty. <br>
&nbsp;&nbsp;  */<br>
protected void addRequestHeaders(URLConnection conn) {<br>
&nbsp;&nbsp;  <br>
}</font></em></p>
<p>在使用 Hessian 进行远程调用时，addRequestHeaders 方法在 sendRequest 时调用，parseResponseHeaders 在成功发送请求后调用。因此我们可以扩展 HessianProxy 类，重新实现这两个方法，在首次调用时获取 Cookie 新，在随后的调用中 将 Cookie 添加到请求头信息中。<br>
除了重新实现 HessianProxy 类外，还需要重新实现 HessianProxyFactory 类的 public Object create(Class api, String urlName, ClassLoader loader) 方法，以便让 HessianProxyFactory 使用重新实现的 HessianProxy 类。</p>
<p>重新实现的代码：<br>
<font color="#0000ff"><em>package example;</em></font></p>
<p><font color="#0000ff"><em>import java.lang.reflect.InvocationHandler;<br>
import java.lang.reflect.Proxy;<br>
import java.net.MalformedURLException;<br>
import java.net.URL;<br>
import java.net.URLConnection;<br>
import java.util.List;</em></font></p>
<p><font color="#0000ff"><em>import com.caucho.hessian.client.HessianProxy;<br>
import com.caucho.hessian.client.HessianProxyFactory;<br>
import com.caucho.hessian.io.HessianRemoteObject;</em></font></p>
<p><font color="#0000ff"><em>public class MyHessianProxyFactory extends HessianProxyFactory {</em></font></p>
<p><font color="#0000ff"><em>@Override<br>
public Object create(Class api, String urlName, ClassLoader loader)<br>
&nbsp;&nbsp;&nbsp;  throws MalformedURLException {<br>
&nbsp;&nbsp;  if (api == null)<br>
&nbsp;&nbsp;&nbsp;  throw new NullPointerException(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &quot;api must not be null for HessianProxyFactory.create()&quot;);<br>
&nbsp;&nbsp;  InvocationHandler handler = null;</em></font></p>
<p><font color="#0000ff"><em>&nbsp;&nbsp;  URL url = new URL(urlName);<br>
&nbsp;&nbsp;  handler = new MyHessianProxy(url, this);</em></font></p>
<p><font color="#0000ff"><em>&nbsp;&nbsp;  return Proxy.newProxyInstance(loader, new Class[] { api,<br>
&nbsp;&nbsp;&nbsp;&nbsp;  HessianRemoteObject.class }, handler);<br>
}<br>
}</em></font></p>
<p><font color="#0000ff"><em>class MyHessianProxy extends HessianProxy {<br>
/** Variable for saving cookie list */<br>
private List&lt;String&gt; cookies = null;</em></font></p>
<p><font color="#0000ff"><em>public MyHessianProxy(URL url, HessianProxyFactory factory) {<br>
&nbsp;&nbsp;  super(url, factory);<br>
}<br>
<br>
/** Get cookie list from the headers of response */<br>
@Override<br>
protected void parseResponseHeaders(URLConnection conn) {<br>
&nbsp;&nbsp;  List&lt;String&gt; _cookies = conn.getHeaderFields().get(&quot;Set-Cookie&quot;);<br>
&nbsp;&nbsp;  if (_cookies != null)<br>
&nbsp;&nbsp;&nbsp;  cookies = _cookies;<br>
}</em></font></p>
<p><font color="#0000ff"><em>/** Add cookie list to request headers*/<br>
@Override<br>
protected void addRequestHeaders(URLConnection conn) {<br>
&nbsp;&nbsp;  if (cookies != null) {<br>
&nbsp;&nbsp;&nbsp;  for (String cookieString : cookies) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  conn.setRequestProperty(&quot;Cookie&quot;, cookieString);<br>
&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;  }<br>
}</em></font></p>
<p><font color="#0000ff"><em>}</em></font></p>
<p>4. 应用举例<br>
接口：<br>
<em><font color="#0000ff">package example;</font></em></p>
<p><em><font color="#0000ff">import java.util.List;</font></em></p>
<p><em><font color="#0000ff">public interface Basic {</font></em></p>
<p><em><font color="#0000ff">public boolean login(String user, String password);<br>
public int giveMeMoney();<br>
public void increaseMyMoney() throws Exception;<br>
}</font></em></p>
<p>实现接口的服务：<br>
<font color="#0000ff"><em>package example;</em></font></p>
<p><font color="#0000ff"><em>import java.util.ArrayList;<br>
import java.util.Enumeration;<br>
import java.util.List;<br>
import javax.servlet.http.HttpServletRequest;<br>
import com.caucho.services.server.ServiceContext;</em></font></p>
<p><font color="#0000ff"><em>public class BasicService implements Basic {</em></font></p>
<p><font color="#0000ff"><em>public boolean login(String user, String password) {<br>
&nbsp;&nbsp;  HttpServletRequest req = (HttpServletRequest) ServiceContext<br>
&nbsp;&nbsp;  .getContextRequest();<br>
&nbsp;&nbsp;  if (user.equals(&quot;user&quot;) &amp;&amp; password.equals(&quot;password&quot;))<br>
&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;  req.getSession().setAttribute(&quot;user&quot;, user);<br>
&nbsp;&nbsp;&nbsp;  return true;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;  return false;<br>
}<br>
public int giveMeMoney()<br>
{<br>
&nbsp;&nbsp;  HttpServletRequest req = (HttpServletRequest) ServiceContext<br>
&nbsp;&nbsp;  .getContextRequest();<br>
&nbsp;&nbsp;  if(req.getSession().getAttribute(&quot;user&quot;) != null)<br>
&nbsp;&nbsp;&nbsp;  return 1000;<br>
&nbsp;&nbsp;  return 0;&nbsp;&nbsp;<br>
}<br>
public void increaseMyMoney() throws Exception<br>
{<br>
&nbsp;&nbsp;  HttpServletRequest req = (HttpServletRequest) ServiceContext<br>
&nbsp;&nbsp;  .getContextRequest();<br>
&nbsp;&nbsp;  if(req.getSession().getAttribute(&quot;user&quot;) != null)<br>
&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;  System.out.println(req.getSession().getAttribute(&quot;user&quot;) + &quot;'s money increased!!!&quot;);<br>
&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;  else<br>
&nbsp;&nbsp;&nbsp;  throw new Exception(&quot;Invalid User&quot;);<br>
&nbsp;&nbsp;&nbsp;<br>
}<br>
}</em></font></p>
<p>客户端程序：<br>
<em><font color="#0000ff">package example;<br>
import java.net.MalformedURLException;</font></em></p>
<p><em><font color="#0000ff">import com.caucho.hessian.client.HessianProxyFactory;</font></em></p>
<p><em><font color="#0000ff">public class BasicClient {<br>
public static void main(String []args) throws MalformedURLException<br>
{<br>
&nbsp;&nbsp;&nbsp;  String url = &quot;</font></em><a href="http://127.0.0.1:8080/hproj/hello"><em><font color="#0000ff">http://127.0.0.1:8080/hproj/hello</font></em></a><em><font color="#0000ff">&quot;;</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;  HessianProxyFactory factory = new MyHessianProxyFactory();<br>
&nbsp;&nbsp;&nbsp;  factory.setUser(&quot;tomcat&quot;);<br>
&nbsp;&nbsp;&nbsp;  factory.setPassword(&quot;tomcat&quot;);<br>
&nbsp;&nbsp;&nbsp;  Basic basic = (Basic) factory.create(Basic.class, url);</font></em></p>
<p><em><font color="#0000ff">&nbsp;&nbsp;&nbsp;  try {<br>
&nbsp;&nbsp;  basic.increaseMyMoney();<br>
} catch (Exception e) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  System.out.println(&quot;ERROR: &quot; + e.getMessage());<br>
}<br>
&nbsp;&nbsp;&nbsp;  basic.login(&quot;user&quot;, &quot;password&quot;);<br>
&nbsp;&nbsp;&nbsp;  System.out.println(basic.giveMeMoney());<br>
&nbsp;&nbsp;&nbsp;  try {<br>
&nbsp;&nbsp;  basic.increaseMyMoney();<br>
} catch (Exception e) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;  System.out.println(&quot;ERROR: &quot; + e.getMessage());<br>
}&nbsp;&nbsp;&nbsp;  <br>
}<br>
}</font></em></p>
<p>在客户端程序中，不在使用原来的 HessianProxyFactory， 而是使用重新实现的 MyHessianProxyFactory 类。</p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/37fe8ffaefe9ad17a9d31153.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/Hessian">Hessian</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/37fe8ffaefe9ad17a9d31153.html#comment">查看评论</a>]]></description>
        <pubDate>2009年06月17日 星期三  11:12</pubDate>
        <category><![CDATA[Hessian]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/37fe8ffaefe9ad17a9d31153.html</guid>
</item>

<item>
        <title><![CDATA[Hessian 机制初探]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/4c4bd9384142752b96ddd80e.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">Hessian 机制初探</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p>Hessian 这个轻量级 Web 服务框架的结构基本如下图所示：</p>
<p><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/4ff6cccf6bf0a718f9dc614c.jpg"></p>
<div forimg="1">
<p><strong>1. Web 服务</strong></p>
<p>Web 服务是通过在 Web 服务器上部署 Servlet 来提供的。在 HessianServlet 的初始化配置中，参数 home-api 用来指定该 Web service 能够提供的服务，参数 home-class 用来指定提供具体服务的类，也即实现了 home-api 所指定接口的类。如果需要提供多个服务，可以部署多个 HessianServlet，并指定相应的 home-api 和 home-class 参数。</p>
<p>HessianServlet 是一个普通的 Servlet。主要接收来自 Hessian 客户端的请求，并将来自客户端 InputStream 和 OutputStream 包装成 Hessian 自己的 Input 和 Output，然后调用 HessianSkeleton 类处理客户的请求、并将处理结果返回给客户端。在初始化的过程中，HessianServlet 会根据参数 home-class 创建一个相应的实例，作为背后真正的 Web 服务提供者相应客户端的请求。<br>
HessianSkeleton 解析来自客户端所请求的方法和参数信息，利用 Java 中的反射机制，调用由 HessianServlet 在初始化过程中创建的 Web 服务实例中对应的方法，然后将结果返回给客户端。</p>
<p><strong>2. 客户端</strong></p>
<p>客户端主要使用了 Java 的动态代理机制。当客户端使用 HessianProxyFactory 创建一个实例时，并不是真正创建了一个 Web 服务接口类型的实力，而是创建了一个 Java 代理实例。随后在调用 Web 服务接口的方法时，实现了 InvocationHandler 接口的 HessianProxy 类将方法名称、参数等信息通过 HttpURLConnection 发送给 Web service 服务器，然后处理服务器的响应并将结果返回。</p>
<p><strong>3. 序列化</strong></p>
<p>客户端与 Hessian 服务通讯时，使用的是 Hessian 定义的二进制协议，除了简单数据类型的传递外，还涉及到对象信息的传递，因此就涉及到对象的序列化问题，也即将对象转换成实在的二进制流通过网络进行传递，以及将收到的二进制流重新转换成对象实例。Hessian 提供了对很多类型的序列化操作，如基本类型：int, double. char, string, 数组等，较为复杂的常用类型：Number, BigDecimal, java.sql.Date, Timestamp, File, Array，Throwable等。对于其他对象类型，Hessian 利用 JavaSerializer 和 JavaDeserializer 来进行序列化操作，不过这些类型需要实现 Serializable 接口，也就是不管这些类型是直接作为参数、还是作为返回值，还是参数或者返回值涉及到这些类型，都必须实现 Serializable。</p>
<p><strong>4. 参考</strong></p>
<p>（1）<a href="http://hi.baidu.com/li_zhongnan/blog/item/a8161fcc0014481800e928c6.html">Hessian 简介</a></p>
<p>（2）<a href="http://hi.baidu.com/li_zhongnan/blog/item/3ff32750346a076a84352419.html">在应用中使用 Hessian</a></p>
<p>（3）<a target="_blank" href="http://hessian.caucho.com/">Hessian 官方网站</a></p>
<p> </p>
</div> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/4c4bd9384142752b96ddd80e.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/Hessian">Hessian</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/4c4bd9384142752b96ddd80e.html#comment">查看评论</a>]]></description>
        <pubDate>2009年06月02日 星期二  16:17</pubDate>
        <category><![CDATA[Hessian]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/4c4bd9384142752b96ddd80e.html</guid>
</item>

<item>
        <title><![CDATA[在应用中使用 Hessian]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/3ff32750346a076a84352419.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">在应用中使用 Hessian</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p>对于 Hessian 的基本介绍，可以参见 <a target="_blank" href="http://hi.baidu.com/li_zhongnan/blog/item/a8161fcc0014481800e928c6.html">Hessian 简介</a>。在应用程序的实际开发中，情况往往比较复杂，但是 Hessian 是一个功能比较强大的 Web service 框架，提供了诸如访问受到 BASIC 认证保护的服务、传递Date、List、Map等常用类型以及对象等复杂数据类型的功能。</p>
<p><strong>1. 使用 Basic 认证保护 Web 服务</strong></p>
<p>如果没有认证机制，那么能够连接到 Web service 服务器上的客户端都有可能访问到 Web 服务。可以利用 Web 服务器的安全机制将 Web service 配置成 Basic 认证模式，这样访问 Web service 的客户端都必须提供用户名和密码才能访问。就 Tomcat 来说，对于在 <a target="_blank" href="http://hi.baidu.com/li_zhongnan/blog/item/a8161fcc0014481800e928c6.html">Hessian 简介</a>中介绍的应用，可以在 web.xml 增加一下内容来设置 hello 服务使用安全认证机制：</p>
<p>&lt;!-- Define reference to the user database for looking up roles --&gt;<br>
&lt;resource-env-ref&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;description&gt;&lt;/description&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;resource-env-ref-name&gt;users&lt;/resource-env-ref-name&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;resource-env-ref-type&gt;org.apache.catalina.UserDatabase&lt;/resource-env-ref-type&gt;<br>
&lt;/resource-env-ref&gt;<br>
<br>
&lt;!-- Define a Security Constraint on this Application --&gt;<br>
&lt;security-constraint&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;web-resource-collection&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;web-resource-name&gt;Hessian Example&lt;/web-resource-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;url-pattern&gt;/hello&lt;/url-pattern&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/web-resource-collection&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;auth-constraint&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;role-name&gt;tomcat&lt;/role-name&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/auth-constraint&gt;&nbsp;&nbsp;&nbsp;  <br>
&lt;/security-constraint&gt;</p>
<p>&lt;!-- Define the Login Configuration for this Application --&gt;<br>
&lt;login-config&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;auth-method&gt;BASIC&lt;/auth-method&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;realm-name&gt;Hessian Example&lt;/realm-name&gt;<br>
&lt;/login-config&gt;</p>
<p>&lt;!-- Security roles referenced by this web application --&gt;<br>
&lt;security-role&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;description&gt;&lt;/description&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;role-name&gt;tomcat&lt;/role-name&gt;<br>
&lt;/security-role&gt;<br>
<br>
这样，只有属于 tomcat 这个角色的用户才能访问 hello 服务。<br>
相应地，在编写 Hessian 客户端程序的时候，就必须提供用户名和密码，才能访问。HessianProxyFactory 提供了 setUser() 和 setPassword() 两个方法用来设置用户名和密码。对于上述应用，应该在代码中添加：</p>
<p>&nbsp;&nbsp;&nbsp;  factory.setUser(&quot;tomcat&quot;);<br>
&nbsp;&nbsp;&nbsp;  factory.setPassword(&quot;tomcat&quot;);<br>
<br>
<strong>2. 让 Web service 返回数组</strong></p>
<p>在 Basic.java 接口中增加方法：</p>
<p>public String[] getNames(); <br>
<br>
在 BasicService.java 中实现该方法：</p>
<p>public String[] getNames() {<br>
&nbsp;&nbsp;  return new String[] { &quot;Apache&quot;, &quot;Tomcat&quot;, &quot;Hessian&quot; };<br>
}</p>
<p>在客户端可以这样访问：</p>
<p>&nbsp;&nbsp;&nbsp;  String[] names = basic.getNames();<br>
&nbsp;&nbsp;&nbsp;  for(int i = 0; i &lt; names.length; i++)<br>
&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  System.out.println(names[i]);<br>
&nbsp;&nbsp;&nbsp;  }</p>
<p><strong>3. 让 Web service 返回一个对象</strong></p>
<p>（1）不成功的做法<br>
创建一个简单 JavaBean 类 Person，只包含三个属性：<br>
package example;</p>
<p>import java.util.Date;</p>
<p>public class Person{<br>
private int id = 0;<br>
private String name = &quot;&quot;;<br>
private Date birthday = null;</p>
<p>public Person(int id, String name, Date birthday) {<br>
&nbsp;&nbsp;  super();<br>
&nbsp;&nbsp;  this.id = id;<br>
&nbsp;&nbsp;  this.name = name;<br>
&nbsp;&nbsp;  this.birthday = birthday;<br>
}<br>
<br>
public int getId() {<br>
&nbsp;&nbsp;  return id;<br>
}<br>
public void setId(int id) {<br>
&nbsp;&nbsp;  this.id = id;<br>
}<br>
public String getName() {<br>
&nbsp;&nbsp;  return name;<br>
}<br>
public void setName(String name) {<br>
&nbsp;&nbsp;  this.name = name;<br>
}<br>
public Date getBirthday() {<br>
&nbsp;&nbsp;  return birthday;<br>
}<br>
public void setBirthday(Date birthday) {<br>
&nbsp;&nbsp;  this.birthday = birthday;<br>
}<br>
<br>
}</p>
<p>在接口中定义：</p>
<p>public Person getPerson(int id);<br>
<br>
在服务类中实现：</p>
<p>public Person getPerson(int id) {<br>
&nbsp;&nbsp;  Person p = null;<br>
&nbsp;&nbsp;  switch (id) {<br>
&nbsp;&nbsp;  case 1:<br>
&nbsp;&nbsp;&nbsp;  p = new Person(1, &quot;Tom&quot;, new java.util.Date(1980, 1, 1));<br>
&nbsp;&nbsp;&nbsp;  break;<br>
&nbsp;&nbsp;  case 2:<br>
&nbsp;&nbsp;&nbsp;  p = new Person(2, &quot;Rose&quot;, new java.util.Date(1981, 1, 1));<br>
&nbsp;&nbsp;&nbsp;  break;<br>
&nbsp;&nbsp;  default:<br>
&nbsp;&nbsp;&nbsp;  break;<br>
&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;  return p;<br>
}</p>
<p>客户端程序<br>
&nbsp;&nbsp;&nbsp;  <br>
&nbsp;&nbsp;&nbsp;  Person p = basic.getPerson(1);<br>
&nbsp;&nbsp;&nbsp;  System.out.println(p.getName());</p>
<p>运行客户端程序，抛出异常：</p>
<p>Exception in thread &quot;main&quot; com.caucho.hessian.client.HessianConnectionException: 500: java.io.IOException: Server returned HTTP response code: 500 for URL: <a href="http://127.0.0.1:8080/htest/hello">http://127.0.0.1:8080/htest/hello</a><br>
at com.caucho.hessian.client.HessianProxy.invoke(HessianProxy.java:202)<br>
at $Proxy0.getPerson(Unknown Source)<br>
at example.BasicClient.main(BasicClient.java:25)</p>
<p>查看服务器日志，得到异常信息：<br>
java.lang.IllegalStateException: Serialized class example.Person must implement java.io.Serializable<br>
at com.caucho.hessian.io.SerializerFactory.getDefaultSerializer(SerializerFactory.java:262)<br>
at com.caucho.hessian.io.SerializerFactory.getSerializer(SerializerFactory.java:234)<br>
at com.caucho.hessian.io.Hessian2Output.writeObject(Hessian2Output.java:406)<br>
...</p>
<p>异常提示 example.Person 必须实现 java.io.Serializable 接口。</p>
<p>（2）Serializable 和序列化<br>
对于简单数据类型，如int, double, char，数组等，以及常用的一些类型，如String，java.util.Date，List, Map等，Hessian 本身对其进行了特殊处理，也就是 Hessian 对其进行了序列化操作，但是 Hessian 不可能了解其他的类以及在您的应用程序中使用的那些类。对于这些类，Hessian 则要求这些类本身能够被序列化，也就是要求这些必须实现 Serializable 接口。</p>
<p>（3）正确的做法<br>
让 Person 实现 Serializable 接口，则 Person.java 应该被修改成：<br>
package example;</p>
<p>import java.io.Serializable;<br>
import java.util.Date;</p>
<p>public class Person implements Serializable{<br>
private int id = 0;<br>
private String name = &quot;&quot;;<br>
private Date birthday = null;<br>
...</p>
<p>重新启动 Web 服务器，就可以成功运行客户端程序了。</p>
<p><strong>4. 让 Web service 返回一个包含 Person 对象 List</strong></p>
<p>在 Basic.java 接口中增加方法：</p>
<p>public List&lt;Person&gt; listPersons();<br>
<br>
在 BasicService.java 中实现该方法：</p>
<p>public List&lt;Person&gt; listPersons() {<br>
&nbsp;&nbsp;  List&lt;Person&gt; list = new ArrayList&lt;Person&gt;();<br>
&nbsp;&nbsp;  list.add(new Person(1, &quot;Tom&quot;, new java.util.Date(1980, 1, 1)));<br>
&nbsp;&nbsp;  list.add(new Person(2, &quot;Rose&quot;, new java.util.Date(1981, 1, 1)));<br>
&nbsp;&nbsp;  return list;<br>
}</p>
<p>在客户端可以这样访问：</p>
<p>&nbsp;&nbsp;&nbsp;  List&lt;Person&gt; list = basic.listPersons();<br>
&nbsp;&nbsp;&nbsp;  System.out.println(list.size()); <br>
&nbsp;&nbsp;&nbsp;</p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/3ff32750346a076a84352419.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/Hessian">Hessian</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/3ff32750346a076a84352419.html#comment">查看评论</a>]]></description>
        <pubDate>2009年05月26日 星期二  15:10</pubDate>
        <category><![CDATA[Hessian]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/3ff32750346a076a84352419.html</guid>
</item>

<item>
        <title><![CDATA[Hessian 简介]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/a8161fcc0014481800e928c6.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">Hessian 简介</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p>说到 web service，首先想到的便是SOAP，对于很多人来说，SOAP基本上就等同于 Web service了。其实，Web service 是一种概念，SOAP 则是这种概念的一种实现而已，除此之外，还有其他针对 Web service 的实现，Hessian 就是其中的一个。<br>
Hessian 与 web service 常用的 SOAP 协议类似，将协议报文封装在HTTP封包中，通过HTTP信道进行传输的。不同的是，Hessian 使用的是二进制协议，而不是像 SOAP 那样使用 XML，使用 Hessian 传输数据量比 SOAP 协议要小很多。Hessian 是一个轻量级的 Web service 实现框架，使用起来也非常简单，只需要将 Hessian 的 jar 包即可编写服务端和客户端的程序，不需要其他附加包。由于其轻量，因此还可以用来编写手机上的应用程序。</p>
<p><strong>1、接口 - Web 服务协议</strong></p>
<p>通常，需要根据应用程序的业务逻辑为 Web service 和其客户端程序交互定义一个协议来描述 Web service 所能提供的服务，在程序实现上，就表现为编写一个接口。Web service 程序实现该接口，客户端按照该接口定义的方法进行业务操作。</p>
<p>package example;</p>
<p>public interface Basic {<br>
public String hello();<br>
}</p>
<p><strong>2、Web 服务程序</strong><br>
<br>
位于服务器端的 Web 服务程序需要实现为业务逻辑定义的接口。</p>
<p>package example;</p>
<p>public class BasicService implements Basic {<br>
public String hello(String name)<br>
{<br>
&nbsp;&nbsp;&nbsp;  return &quot;Hello, &quot; + name;<br>
}<br>
}</p>
<p><strong>3、Web 服务器配置</strong></p>
<p>Hessian 通过在 Web 服务器上配置 HessianServlet 这个 Servlet 来提供 Web service。为 HessianServlet 指定不同的 init-param 来使 HessianServlet 提供不同的 Web service 服务。</p>
<p>&lt;web-app&gt;<br>
&lt;servlet&gt;<br>
&nbsp;&nbsp;  &lt;servlet-name&gt;hello&lt;/servlet-name&gt;<br>
&nbsp;&nbsp;  &lt;servlet-class&gt;com.caucho.hessian.server.HessianServlet&lt;/servlet-class&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;init-param&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;param-name&gt;home-class&lt;/param-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;param-value&gt;example.BasicService&lt;/param-value&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/init-param&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;init-param&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;param-name&gt;home-api&lt;/param-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;param-value&gt;example.Basic&lt;/param-value&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/init-param&gt;<br>
&lt;/servlet&gt;</p>
<p>&lt;servlet-mapping&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;url-pattern&gt;/hello&lt;/url-pattern&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;servlet-name&gt;hello&lt;/servlet-name&gt;<br>
&lt;/servlet-mapping&gt;<br>
&lt;/web-app&gt;</p>
<p>其中，home-class 指示实现接口的类名称，home-api 指示描述 Web service 的接口名称。</p>
<p><strong>4、客户端程序</strong></p>
<p>在 Java 客户端使用 Hessian service 就像调用一个普通的 Java 方法那样简单。通过指定目标 web service 的 URL 和描述 Web service 的接口，HessianProxyFactory 将创建一个实现了该接口的对象，然后就可以像使用本地对象那样使用该对象了。</p>
<p>package example;</p>
<p>import com.caucho.hessian.client.HessianProxyFactory;</p>
<p>public class BasicClient {<br>
public static void main(String []args)<br>
&nbsp;&nbsp;&nbsp;  throws Exception<br>
{<br>
&nbsp;&nbsp;&nbsp;  String url = &quot;<a href="http://127.0.0.1:8080/htest/hello">http://127.0.0.1:8080/htest/hello</a>&quot;;</p>
<p>&nbsp;&nbsp;&nbsp;  HessianProxyFactory factory = new HessianProxyFactory();<br>
&nbsp;&nbsp;&nbsp;  Basic basic = (Basic) factory.create(Basic.class, url);</p>
<p>&nbsp;&nbsp;&nbsp;  System.out.println(basic.hello(&quot;hessian&quot;));<br>
}<br>
}</p>
<p><strong>5、参考</strong></p>
<p><a href="http://hessian.caucho.com/">http://hessian.caucho.com/</a></p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/a8161fcc0014481800e928c6.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/Hessian">Hessian</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/a8161fcc0014481800e928c6.html#comment">查看评论</a>]]></description>
        <pubDate>2009年05月21日 星期四  15:26</pubDate>
        <category><![CDATA[Hessian]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/a8161fcc0014481800e928c6.html</guid>
</item>

<item>
        <title><![CDATA[LCOV README 文件（翻译）]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/8a386bca49b4d241f31fe737.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">LCOV README 文件（翻译）</font></strong></p>
<p> </p>
<p><strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong></p>
<p>-------------------------------------------------<br>
- README file for the LTP GCOV extension (LCOV) -<br>
- Last changes: 2008-11-17&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  -<br>
-------------------------------------------------<br>
<br>
说明<br>
-----------<br>
GCOV 是一个 GNU 工具，当运行一个特定的测试用例后，通过GCOV 可以查看一个程序的那些代码被实际执行。 LCOV 是GCOV的一个扩展工具。该扩展由一套 PERL 脚本组成，基于 GCOV 的文本式输出来实现以下增强的功能：</p>
<p><br>
&nbsp;&nbsp;&nbsp;  * 基于 HTML 的输出：使用条形图和不同的颜色来表示。<br>
<br>
&nbsp;&nbsp;&nbsp;  * 支持大型项目：信息汇总页面提供三个层次的代码覆盖细节信息：目录视图、文件视图和源代码视图，允许快速浏览代码覆盖数据。<br>
<br>
LCOV 最初为支持 Linux 内核代码覆盖评估而设计，但是同样可用于测量标准用户空间应用程序的代码覆盖率。<br>
<br>
<br>
README 目录<br>
-----------------------<br>
1. 相关文件<br>
2. 安装 LCOV<br>
3. 如何访问内核代码覆盖数据的示例<br>
4. 如何访问用户空间应用程序代码覆盖数据的示例<br>
5. 问题和评论<br>
<br>
<br>
<br>
1. 相关文件<br>
------------------<br>
README&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 该 README 文件<br>
CHANGES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 各发行版本的更新列表<br>
bin/lcov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 收集 LCOV 代码覆盖数据的工具<br>
bin/genhtml&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 根据 LCOV 数据生成 HTML 输出的工具<br>
bin/gendesc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 创建说明文件的工具，同时也被 genhtml 使用<br>
bin/geninfo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 内部工具（创建 LCOV 数据文件）<br>
bin/genpng&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 内部工具（创建源代码文件的 png 图形汇总信息）<br>
bin/install.sh&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 内部工具（安装和卸载）<br>
descriptions.tests - LTP 软件包的测试说明文件，被 gendesc 使用<br>
man&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 放置 LCOV 使用工具的 man 页面的目录<br>
example&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - 用来演示 LCOV 的示例文件目录<br>
lcovrc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - LCOV 配置文件<br>
Makefile&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  - Makefile 文件，包含 'install' 和 'uninstall' 目标<br>
<br>
<br>
2. 安装 LCOV<br>
------------------<br>
可以从以下页面选择下载 LCOV 软件包，RPM 或 tarball 形式：<br>
&nbsp;&nbsp;&nbsp;&nbsp;  <br>
<a href="http://ltp.sourceforge.net/coverage/lcov.php">http://ltp.sourceforge.net/coverage/lcov.php</a><br>
<br>
为安装 tarball，将软件包解压到一个目录然后运行：<br>
<br>
make install<br>
<br>
也可以使用匿名 CVS 来获取最新版本（但可能是不稳定的版本）：<br>
<br>
cvs -d:pserver:anonymous@ltp.cvs.sourceforge.net:/cvsroot/ltp login<br>
<br>
（当询问密码时，按回车即可）<br>
<br>
cvs -z3 -d:pserver:anonymous@ltp.cvs.sourceforge.net:/cvsroot/ltp export -D now utils<br>
<br>
切换到 utils/analysis/lcov 目录然后输入：<br>
<br>
make install<br>
<br>
<br>
3. 如何访问内核代码覆盖数据的示例<br>
---------------------------------------------------<br>
前提条件：从以下页面下载并安装 gcov-kernel 软件包：<br>
<br>
<a href="http://sourceforge.net/projects/ltp">http://sourceforge.net/projects/ltp</a><br>
<br>
将最终的 gcov 内核模块文件复制到 system wide modules 目录或者 PERL 脚本所在目录。以 root 身份， 执行：<br>
<br>
a) 重置计数器<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;  lcov --zerocounters<br>
<br>
b) 收集当前代码覆盖状态到一个文件<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;  lcov --capture --output-file kernel.info<br>
<br>
c) 获取 HTML 输出<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;  genhtml kernel.info<br>
<br>
使用 web 浏览器打开 index.html 文件查看代码覆盖结果。<br>
<br>
<br>
4. 如何访问用户空间应用程序代码覆盖数据的示例<br>
---------------------------------------------------------------------<br>
前提条件：使用 GCC 以 -fprofile-arcs 和-ftest-coverage 选项编译程序。假设编译目录名称为 &quot;appdir&quot;，然后执行：<br>
<br>
a) 重置计数器<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;  lcov --directory appdir --zerocounters<br>
<br>
b) 收集当前代码覆盖状态到一个文件（应用程序启动和停止至少一次后，该命令才能正常工作）<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;  lcov --directory appdir --capture --output-file app.info<br>
<br>
c) 获取 HTML 输出<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;  genhtml app.info<br>
<br>
使用 web 浏览器打开 index.html 文件查看代码覆盖结果。<br>
<br>
<br>
5. 问题和评论<br>
-------------------------<br>
参见软件包中的 man 页面，以便获取如何使用 LCOV 工具的详细信息。<br>
<br>
有关这些工具的问题和评论，请通过电子邮件联系，LTP 电子邮件列表：<a href="mailto:ltp-coverage@lists.sourceforge.net">ltp-coverage@lists.sourceforge.net</a>。</p>
<p> </p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/8a386bca49b4d241f31fe737.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/c%D3%EF%D1%D4">c语言</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/8a386bca49b4d241f31fe737.html#comment">查看评论</a>]]></description>
        <pubDate>2009年02月10日 星期二  13:08</pubDate>
        <category><![CDATA[c语言]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/8a386bca49b4d241f31fe737.html</guid>
</item>

<item>
        <title><![CDATA[LCOV 简介（翻译）]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/5043683f4d266ac57c1e71e2.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">LCOV 简介（翻译）</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<h1>LCOV - LTP GCOV 扩展</h1>
<h2>关于LCOV</h2>
<p style="padding-bottom: 10px">LCOV 是一个建立在GCC代码覆盖率测试工具 <a href="http://gcc.gnu.org/onlinedocs/gcc/Gcov.html">gcov</a> 上的图形化工具。它收集源代码文件的 gcov 数据，生成包含源代码、标注有代码覆盖信息的 HTML 页面。同时为了方便浏览，也添加了信息汇总页面。Example output</p>
<p>一个小示例项目的 <a href="http://ltp.sourceforge.net/coverage/lcov/output/index.html">HTML output</a> 。</p>
<p>
<table class="FCK__ShowTableBorders">
    <tbody>
        <tr>
            <td width="320"><a href="http://ltp.sourceforge.net/coverage/lcov/output/index.html"><img style="border-right: 1px solid; border-top: 1px solid; border-left: 1px solid; border-bottom: 1px solid" height="228" alt="Screenshot of an overview page generated by LCOV" width="320" border="1" src="http://ltp.sourceforge.net/coverage/lcov/screenshot1.gif"></a></td>
            <td width="40"> </td>
            <td width="320"><a href="http://ltp.sourceforge.net/coverage/lcov/output/example/methods/iterate.c.gcov.html"><img style="border-right: 1px solid; border-top: 1px solid; border-left: 1px solid; border-bottom: 1px solid" height="228" alt="Screenshot of a page of source code annotated with coverage information generated by LCOV" width="320" border="1" src="http://ltp.sourceforge.net/coverage/lcov/screenshot2.gif"></a></td>
        </tr>
        <tr>
            <td><span style="font-style: italic">截图 1：信息汇总页面</span></td>
            <td> </td>
            <td><span style="font-style: italic">截图 2：标注有代码覆盖率信息的源代码</span></td>
        </tr>
    </tbody>
</table>
</p>
<p style="padding-bottom: 10px"><span style="font-weight: bold">注：</span>该示例的源代码和 Makefile 文件包含在 .tar.gz 文件中（参见下载章节）。</p>
<h2>下载</h2>
<p><span style="font-weight: bold">最新版本：</span>LCOV 1.7 （<a href="http://ltp.sourceforge.net/coverage/lcov/changes.php">更新日志</a>）。</p>
<ul>
    <li><a href="http://downloads.sourceforge.net/ltp/lcov-1.7-1.noarch.rpm">lcov-1.7-1.noarch.rpm</a></li>
</ul>
<p><span style="font-weight: bold">源代码：</span>LCOV 按照 GPL 许可协议发布。</p>
<ul>
    <li><a href="http://downloads.sourceforge.net/ltp/lcov-1.7-1.src.rpm">lcov-1.7-1.src.rpm</a></li>
    <li><a href="http://downloads.sourceforge.net/ltp/lcov-1.7.tar.gz">lcov-1.7.tar.gz</a></li>
    <li><a href="http://sourceforge.net/cvs/?group_id=3382">CVS 访问</a>：模块名称为 &quot;utils&quot;，目录为 &quot;utils/analysis/lcov&quot;</li>
    <li><a href="http://ltp.cvs.sourceforge.net/ltp/utils/analysis/lcov/">浏览 CVS</a></li>
</ul>
<h2 style="padding-bottom: 10px; padding-top: 15px">文档</h2>
<ul>
    <li><a href="http://ltp.sourceforge.net/coverage/lcov/readme.php">README 文件</a></li>
    <li><span style="font-weight: bold">man 手册：</span><a href="http://ltp.sourceforge.net/coverage/lcov/lcov.1.php">lcov.1</a>, <a href="http://ltp.sourceforge.net/coverage/lcov/genhtml.1.php">genhtml.1</a>, <a href="http://ltp.sourceforge.net/coverage/lcov/lcovrc.5.php">lcovrc</a>, <a href="http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php">geninfo.1</a>, <a href="http://ltp.sourceforge.net/coverage/lcov/gendesc.1.php">gendesc.1</a>, <a href="http://ltp.sourceforge.net/coverage/lcov/genpng.1.php">genpng.1</a></li>
</ul>
<h2 style="padding-top: 15px">评论和问题</h2>
<p>有关评论和问题，请联系：<a href="mailto:ltp-coverage@lists.sourceforge.net">ltp-coverage@lists.sourceforge.net</a></p>
<p>------------------------------------------------------------------------------------------------</p>
<p>原文档地址：</p>
<p><a href="http://ltp.sourceforge.net/coverage/lcov.php">http://ltp.sourceforge.net/coverage/lcov.php</a></p>
<p> </p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/5043683f4d266ac57c1e71e2.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/c%D3%EF%D1%D4">c语言</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/5043683f4d266ac57c1e71e2.html#comment">查看评论</a>]]></description>
        <pubDate>2009年02月06日 星期五  09:16</pubDate>
        <category><![CDATA[c语言]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/5043683f4d266ac57c1e71e2.html</guid>
</item>

<item>
        <title><![CDATA[C语言写的trim()函数]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/f4c5b0f3eba7b6cc0b46e025.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">C语言写的trim()函数</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>C语言的标准库中缺少对字符串进行操作的trim()函数，使用起来有些不便，可以使用利用 strlen 和 isspace 函数以及指针来自己写一个。</p>
<p>1、strlen 函数</p>
<p>  原型：extern int strlen(char *s);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  <br>
  用法：#include &lt;string.h&gt;<br>
  <br>
  功能：计算字符串s的长度<br>
  <br>
  说明：返回s的长度，不包括结束符NULL。</p>
<p>2、isspace 函数</p>
<p>  原型：extern int isspace(int c);<br>
  <br>
  用法：#include &lt;ctype.h&gt;<br>
  <br>
  功能：判断字符c是否为空白符<br>
  <br>
  说明：当c为空白符时，返回非零值，否则返回零。<br>
  　　　空白符指空格、水平制表、垂直制表、换页、回车和换行符。</p>
<p>3、trim 函数</p>
<p><font color="#0000ff">#include &lt;string.h&gt;<br>
#include &lt;ctype.h&gt;</font></p>
<p><font color="#0000ff">char *trim(char *str)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  char *p = str;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  char *p1;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if(p)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  p1 = p + strlen(str) - 1;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  while(*p &amp;&amp; isspace(*p)) p++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  while(p1 &gt; p &amp;&amp; isspace(*p1)) *p1-- = '\0';<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return p;<br>
}</font></p>
<p>4、应用举例</p>
<p><font color="#0000ff">int main()<br>
{<br>
 int i = 0;<br>
 char strs[][128] = {<br>
&nbsp;&nbsp; NULL,<br>
&nbsp;&nbsp; &quot;&quot;,<br>
&nbsp;&nbsp; &quot; &quot;,<br>
&nbsp;&nbsp; &quot;hello world&quot;,<br>
&nbsp;&nbsp; &quot; hello&quot;,<br>
&nbsp;&nbsp; &quot;hello world &quot;,<br>
&nbsp;&nbsp; &quot; hello world &quot;,<br>
&nbsp;&nbsp; &quot;\t\n\thello world &quot;,<br>
&nbsp;&nbsp; &quot;END&quot;<br>
 };<br>
 do<br>
 {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  printf(&quot;trim(\&quot;%s\&quot;)=%s.\n&quot;, strs[i], trim(strs[i]));<br>
 }while(strcmp(strs[i++], &quot;END&quot;));</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return 0;<br>
}</font></p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/f4c5b0f3eba7b6cc0b46e025.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/c%D3%EF%D1%D4">c语言</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/f4c5b0f3eba7b6cc0b46e025.html#comment">查看评论</a>]]></description>
        <pubDate>2009年02月05日 星期四  17:10</pubDate>
        <category><![CDATA[c语言]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/f4c5b0f3eba7b6cc0b46e025.html</guid>
</item>

<item>
        <title><![CDATA[基于JRobin的磁盘IO监控]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/5c7ff6ed157c16d3b31cb187.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">基于JRobin的磁盘IO监控</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>先列以下以前写过的文章作为参考：</p>
<p>1。<a target="_blank" href="http://hi.baidu.com/li_zhongnan/blog/item/6fef0499a408940d6e068cbf.html">JRobin简介</a></p>
<p>2。<a target="_blank" href="http://hi.baidu.com/li_zhongnan/blog/item/7b311351dba585898d543029.html">基于JRobin的网络监控管理</a>，以及<a target="_blank" href="http://hi.baidu.com/li_zhongnan/blog/item/13016dc855bc59117e3e6f69.html">基于JRobin的CPU使用率监控</a></p>
<p>3。<a target="_blank" href="http://hi.baidu.com/li_zhongnan/blog/item/f4c5b0f30b7f56cc0b46e0ed.html">磁盘IO性能监控（Linux 和 Windows）</a></p>
<p>这些文章介绍了JRobin、利用JRobin以及WIndows脚本技术监控网络和CPU使用率，以及如何获取Linux以及Windows下磁盘IO性能数据。将这些结合在一起，于是就有了使用Java语言写成的基于JRobin的磁盘IO监控代码。</p>
<p>监控程序的代码与利用ping监控网络和监控CPU基本类似，但是还是有一些不同：</p>
<p>1。由于可能有多个逻辑磁盘，因此需要先获取磁盘列表，然后才即每个磁盘的IO数据。</p>
<p>2。出于性能考虑，特别是在Windows下，在获取磁盘IO数据时，可以在执行脚本时一次性采集这些数据，根据磁盘名称保存，不用分别获取，这样可以提高性能。</p>
<p>3。数据的单位和显示在图形上的格式不同。</p>
<p>4。JRobin对数据源名称及其程度有要求，因此需要对磁盘名称进行一定的变更。</p>
<p><strong>成果：</strong></p>
<div forimg="1"><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/35d00c51c2571f06377abe5d.jpg"></div>
<p><strong>Java代码：</strong></p>
<p><font color="#0000ff">import java.awt.Color;<br>
import java.awt.Font;<br>
import java.io.BufferedReader;<br>
import java.io.File;<br>
import java.io.IOException;<br>
import java.io.InputStreamReader;<br>
import java.io.PrintWriter;<br>
import java.util.ArrayList;<br>
import java.util.Date;<br>
import java.util.HashMap;<br>
import java.util.List;<br>
import java.util.Timer;<br>
import java.util.TimerTask;<br>
import java.util.logging.Logger;</font></p>
<p><font color="#0000ff">import org.jrobin.core.RrdDb;<br>
import org.jrobin.core.RrdDef;<br>
import org.jrobin.core.Sample;<br>
import org.jrobin.graph.RrdGraph;<br>
import org.jrobin.graph.RrdGraphDef;</font></p>
<p><font color="#0000ff">public class DiskIoMonitor {<br>
 String[] disks = null;</font></p>
<p><font color="#0000ff"> public static String[] execute(String[] commands) {<br>
&nbsp;&nbsp; String[] strs = null;<br>
&nbsp;&nbsp; File scriptFile = null;<br>
&nbsp;&nbsp; try {<br>
&nbsp;&nbsp;&nbsp; List&lt;String&gt; cmdList = new ArrayList&lt;String&gt;();<br>
&nbsp;&nbsp;&nbsp; String osName = System.getProperty(&quot;os.name&quot;);<br>
&nbsp;&nbsp;&nbsp; if (osName.indexOf(&quot;Windows&quot;) &gt; -1) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; scriptFile = File.createTempFile(&quot;monitor&quot;, &quot;.vbs&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; cmdList.add(&quot;CMD.EXE&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; cmdList.add(&quot;/C&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; cmdList.add(&quot;CSCRIPT.EXE&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; cmdList.add(&quot;//NoLogo&quot;);<br>
&nbsp;&nbsp;&nbsp; } else {<br>
&nbsp;&nbsp;&nbsp;&nbsp; scriptFile = File.createTempFile(&quot;monitor&quot;, &quot;.sh&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; cmdList.add(&quot;/bin/bash&quot;);<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; String fileName = scriptFile.getCanonicalPath();<br>
&nbsp;&nbsp;&nbsp; PrintWriter writer = new PrintWriter(scriptFile);<br>
&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; commands.length; i++) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; writer.println(commands[i]);<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; writer.flush();<br>
&nbsp;&nbsp;&nbsp; writer.close();<br>
&nbsp;&nbsp;&nbsp; cmdList.add(fileName);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; writer.flush();<br>
&nbsp;&nbsp;&nbsp; writer.close();<br>
&nbsp;&nbsp;&nbsp; cmdList.add(fileName);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; ProcessBuilder pb = new ProcessBuilder(cmdList);<br>
&nbsp;&nbsp;&nbsp; Process p = pb.start();</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; p.waitFor();</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; String line = null;<br>
&nbsp;&nbsp;&nbsp; BufferedReader stdout = new BufferedReader(new InputStreamReader(p<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .getInputStream()));<br>
&nbsp;&nbsp;&nbsp; List&lt;String&gt; stdoutList = new ArrayList&lt;String&gt;();<br>
&nbsp;&nbsp;&nbsp; while ((line = stdout.readLine()) != null) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; stdoutList.add(line);<br>
&nbsp;&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; BufferedReader stderr = new BufferedReader(new InputStreamReader(p<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .getErrorStream()));<br>
&nbsp;&nbsp;&nbsp; List&lt;String&gt; stderrList = new ArrayList&lt;String&gt;();<br>
&nbsp;&nbsp;&nbsp; while ((line = stderr.readLine()) != null) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; stderrList.add(line);<br>
&nbsp;&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; strs = stdoutList.toArray(new String[0]);<br>
&nbsp;&nbsp; } catch (Exception e) {<br>
&nbsp;&nbsp;&nbsp; e.printStackTrace();<br>
&nbsp;&nbsp; } finally {<br>
&nbsp;&nbsp;&nbsp; if (scriptFile != null)<br>
&nbsp;&nbsp;&nbsp;&nbsp; scriptFile.delete();<br>
&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; return strs;<br>
 }</font></p>
<p><font color="#0000ff"> private String dataFormat = &quot;%5.1lf%sB&quot;;<br>
 private Logger logger = Logger.getLogger(this.getClass().getName());<br>
 private String monitorName = &quot;diskio&quot;;<br>
 private String dataDir = &quot;.&quot;;<br>
 private int step = 10;<br>
 private String rrdPath = &quot;&quot;;<br>
 private Timer timer = new Timer();<br>
 private long timeStart = 0;<br>
 protected int width = 600;<br>
 protected int height = 150;</font></p>
<p><font color="#0000ff"> public DiskIoMonitor() {<br>
&nbsp;&nbsp; this(null);<br>
 }</font></p>
<p><font color="#0000ff"> public DiskIoMonitor(String diskName) {<br>
&nbsp;&nbsp; this.rrdPath = this.dataDir + File.separator + monitorName + &quot;.rrd&quot;;</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; disks = getDisks();<br>
&nbsp;&nbsp; if (diskName != null) {<br>
&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; disks.length; i++) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (diskName.equals(disks[i])) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; disks = new String[] { diskName };<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }<br>
&nbsp;&nbsp; if (disks == null)<br>
&nbsp;&nbsp;&nbsp; disks = new String[0];<br>
 }</font></p>
<p><font color="#0000ff"> public String generateGraph() {<br>
&nbsp;&nbsp; long timeCur = org.jrobin.core.Util.getTimestamp();<br>
&nbsp;&nbsp; return generateGraph(timeStart, timeCur);<br>
 }</font></p>
<p><font color="#0000ff"> public String generateGraph(long start, long end) {<br>
&nbsp;&nbsp; RrdDb rrdDb = null;<br>
&nbsp;&nbsp; try {<br>
&nbsp;&nbsp;&nbsp; Color[] colors = new Color[] { Color.GREEN, Color.BLUE,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Color.MAGENTA, Color.YELLOW, Color.RED, Color.CYAN,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Color.ORANGE, Color.PINK, Color.BLACK };<br>
&nbsp;&nbsp;&nbsp; String graphPath = this.dataDir + File.separator + monitorName<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + &quot;.png&quot;;</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; // create graph<br>
&nbsp;&nbsp;&nbsp; logger.info(&quot;Creating graph&quot;);<br>
&nbsp;&nbsp;&nbsp; RrdGraphDef gDef = new RrdGraphDef();<br>
&nbsp;&nbsp;&nbsp; gDef.setWidth(width);<br>
&nbsp;&nbsp;&nbsp; gDef.setHeight(height);<br>
&nbsp;&nbsp;&nbsp; gDef.setFilename(graphPath);<br>
&nbsp;&nbsp;&nbsp; gDef.setStartTime(start);<br>
&nbsp;&nbsp;&nbsp; gDef.setEndTime(end);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; gDef.setTitle(&quot;Disk IO&quot;);<br>
&nbsp;&nbsp;&nbsp; gDef.setVerticalLabel(&quot;Byte/s&quot;);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; String[] dsNames = null;</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; rrdDb = new RrdDb(rrdPath);<br>
&nbsp;&nbsp;&nbsp; dsNames = rrdDb.getDsNames();</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; dsNames.length; i++) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; String dsName = dsNames[i];<br>
&nbsp;&nbsp;&nbsp;&nbsp; String legend = dsName;<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (legend == null || legend.equals(&quot;&quot;))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; legend = dsName;<br>
&nbsp;&nbsp;&nbsp;&nbsp; gDef.datasource(dsName, rrdPath, dsName, &quot;AVERAGE&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; gDef.line(dsName, colors[i % colors.length], legend, 2);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp; gDef.gprint(dsName, &quot;MIN&quot;, dataFormat + &quot; Min&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; gDef.gprint(dsName, &quot;AVERAGE&quot;, dataFormat + &quot; Avg&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; gDef.gprint(dsName, &quot;MAX&quot;, dataFormat + &quot; Max&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; gDef.gprint(dsName, &quot;LAST&quot;, dataFormat + &quot; Last\\r&quot;);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp; gDef.print(dsName, &quot;MIN&quot;, &quot;min&quot; + dsName + &quot; = %.3f&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; gDef.print(dsName, &quot;AVERAGE&quot;, &quot;avg&quot; + dsName + &quot; = %.3f&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; gDef.print(dsName, &quot;MAX&quot;, &quot;max&quot; + dsName + &quot; = %.3f&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; gDef.print(dsName, &quot;LAST&quot;, &quot;last&quot; + dsName + &quot; = %.3f&quot;);<br>
&nbsp;&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; gDef.setImageInfo(&quot;&lt;img src='%s' width='%d' height = '%d'&gt;&quot;);<br>
&nbsp;&nbsp;&nbsp; gDef.setPoolUsed(false);<br>
&nbsp;&nbsp;&nbsp; gDef.setImageFormat(&quot;png&quot;);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; gDef.setSmallFont(new Font(&quot;Monospaced&quot;, Font.PLAIN, 11));<br>
&nbsp;&nbsp;&nbsp; gDef.setLargeFont(new Font(&quot;SansSerif&quot;, Font.BOLD, 14));<br>
&nbsp;&nbsp;&nbsp; // gDef.setAltYMrtg(true);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; // create graph finally<br>
&nbsp;&nbsp;&nbsp; RrdGraph graph = new RrdGraph(gDef);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; // logger.info(graph.getRrdGraphInfo().dump());<br>
&nbsp;&nbsp;&nbsp; logger.info(&quot;Graph created&quot;);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; return graph.getRrdGraphInfo().getFilename();<br>
&nbsp;&nbsp; } catch (Exception e) {<br>
&nbsp;&nbsp;&nbsp; logger.warning(&quot;Error in generating graph: &quot; + e.getMessage());<br>
&nbsp;&nbsp; } finally {<br>
&nbsp;&nbsp;&nbsp; if (rrdDb != null)<br>
&nbsp;&nbsp;&nbsp;&nbsp; try {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rrdDb.close();<br>
&nbsp;&nbsp;&nbsp;&nbsp; } catch (IOException e) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }<br>
&nbsp;&nbsp; return null;<br>
 }</font></p>
<p><font color="#0000ff"> /**<br>
  * Return a HashMap which contains the current value of each data source.<br>
  * <br>
  * @return the current value of each data source.<br>
  */<br>
 public HashMap&lt;String, Double&gt; getValues() {<br>
&nbsp;&nbsp; String osName = System.getProperty(&quot;os.name&quot;);<br>
&nbsp;&nbsp; if (osName.indexOf(&quot;Windows&quot;) &gt; -1) {<br>
&nbsp;&nbsp;&nbsp; return getWinDSValues();<br>
&nbsp;&nbsp; } else {<br>
&nbsp;&nbsp;&nbsp; return getLinuxDSValues();<br>
&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff"> }</font></p>
<p><font color="#0000ff"> /**<br>
  * Initialization.<br>
  */<br>
 public void initialize() throws Exception {<br>
&nbsp;&nbsp; RrdDb rrdDb = null;<br>
&nbsp;&nbsp; try {<br>
&nbsp;&nbsp;&nbsp; rrdDb = new RrdDb(rrdPath);<br>
&nbsp;&nbsp; } catch (Exception e) {<br>
&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; if (rrdDb == null) {<br>
&nbsp;&nbsp;&nbsp; logger.info(&quot;RRD data is not located in &quot; + rrdPath<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + &quot;, create a new one&quot;);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; RrdDef rrdDef = new RrdDef(rrdPath, timeStart - 1, step);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; disks.length; i++)<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp; rrdDef.addDatasource(disks[i] + &quot;read&quot;, &quot;GAUGE&quot;, 2 * step, 0, Double.NaN); <br>
&nbsp;&nbsp;&nbsp;&nbsp; rrdDef.addDatasource(disks[i] + &quot;write&quot;, &quot;GAUGE&quot;, 2 * step, 0, Double.NaN);&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp; rrdDef.addArchive(&quot;AVERAGE&quot;, 0.5, 1, 24 * 3600 / step);<br>
&nbsp;&nbsp;&nbsp; rrdDef.addArchive(&quot;AVERAGE&quot;, 0.5, 300 / step, 7 * 288);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; logger.info(&quot;Estimated file size: &quot; + rrdDef.getEstimatedSize());<br>
&nbsp;&nbsp;&nbsp; rrdDb = new RrdDb(rrdDef);<br>
&nbsp;&nbsp;&nbsp; logger.info(&quot;RRD file created.&quot;);<br>
&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; logger.info(monitorName + &quot; RRD Db Defs: &quot; + rrdDb.getRrdDef().dump());<br>
&nbsp;&nbsp; if (rrdDb != null)<br>
&nbsp;&nbsp;&nbsp; rrdDb.close();<br>
 }</font></p>
<p><font color="#0000ff"> /**<br>
  * Start monitor.<br>
  * <br>
  * @return true if succeed, else false.<br>
  */<br>
 public boolean start() {<br>
&nbsp;&nbsp; logger.info(&quot;start to monitor &quot; + monitorName);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; try {<br>
&nbsp;&nbsp;&nbsp; timeStart = org.jrobin.core.Util.getTimestamp();<br>
&nbsp;&nbsp;&nbsp; initialize();<br>
&nbsp;&nbsp;&nbsp; timer.scheduleAtFixedRate(new TimerTask() {<br>
&nbsp;&nbsp;&nbsp;&nbsp; public void run() {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; updateData();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception e) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger.severe(&quot;Timer running error: &quot; + e.getMessage());<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }, 0, step * 1000);<br>
&nbsp;&nbsp;&nbsp; return true;<br>
&nbsp;&nbsp; } catch (Exception e) {<br>
&nbsp;&nbsp;&nbsp; e.printStackTrace();<br>
&nbsp;&nbsp; }<br>
&nbsp;&nbsp; return false;<br>
 }</font></p>
<p><font color="#0000ff"> /**<br>
  * Stop monitor.<br>
  */<br>
 public void stop() {<br>
&nbsp;&nbsp; timer.cancel();<br>
 }</font></p>
<p><font color="#0000ff"> private void updateData() throws Exception {<br>
&nbsp;&nbsp; RrdDb rrdDb = null;<br>
&nbsp;&nbsp; try {<br>
&nbsp;&nbsp;&nbsp; logger.info(&quot;update rrd data for &quot; + monitorName);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; rrdDb = new RrdDb(rrdPath);<br>
&nbsp;&nbsp;&nbsp; String[] dsNames = rrdDb.getDsNames();</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp; long lastUpdateTime = rrdDb.getLastUpdateTime();<br>
&nbsp;&nbsp;&nbsp; long t = org.jrobin.core.Util.getTimestamp();<br>
&nbsp;&nbsp;&nbsp; if (t &gt; lastUpdateTime) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; rrdDb.setInfo(&quot;T=&quot; + t);<br>
&nbsp;&nbsp;&nbsp;&nbsp; Sample sample = rrdDb.createSample();<br>
&nbsp;&nbsp;&nbsp;&nbsp; sample.setTime(t);<br>
&nbsp;&nbsp;&nbsp;&nbsp; HashMap&lt;String, Double&gt; hm = getValues();<br>
&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(hm);<br>
&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; dsNames.length; i++) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String dsName = dsNames[i];<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Double value = hm.get(dsName);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger.fine(dsName + &quot; = &quot; + value);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (value == null)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value = Double.NaN;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.setValue(dsName, value);<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp; sample.update();<br>
&nbsp;&nbsp;&nbsp; } else {<br>
&nbsp;&nbsp;&nbsp;&nbsp; logger.warning(&quot;Bad sample time &quot; + t + &quot;(&quot;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + new Date(t * 1000L) + &quot;)&quot; + new Date()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + &quot;, the last update time was &quot; + lastUpdateTime + &quot;(&quot;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + new Date(lastUpdateTime * 1000L) + &quot;) - &quot;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + monitorName);<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; } finally {<br>
&nbsp;&nbsp;&nbsp; if (rrdDb != null)<br>
&nbsp;&nbsp;&nbsp;&nbsp; try {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rrdDb.close();<br>
&nbsp;&nbsp;&nbsp;&nbsp; } catch (IOException e) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }<br>
 }</font></p>
<p><font color="#0000ff"> private String[] getDisks() {<br>
&nbsp;&nbsp; String[] scripts = null;<br>
&nbsp;&nbsp; String osName = System.getProperty(&quot;os.name&quot;);<br>
&nbsp;&nbsp; if (osName.indexOf(&quot;Windows&quot;) &gt; -1) {<br>
&nbsp;&nbsp;&nbsp; scripts = new String[] {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;strComputer = \&quot;.\&quot;&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Set objWMIService = GetObject(\&quot;winmgmts:\&quot; _&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;&nbsp;&nbsp; &amp; \&quot;{impersonationLevel=impersonate}!\\\\\&quot; &amp; strComputer &amp; \&quot;</font><a href="file://root//cimv2/"><font color="#0000ff">\\root\\cimv2\</font></a><font color="#0000ff">&quot;)&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Const HARD_DISK = 3&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Set colDisks = objWMIService.ExecQuery _&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot; (\&quot;Select * from Win32_LogicalDisk Where DriveType = \&quot; &amp; HARD_DISK)&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;For Each objDisk in colDisks&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot; Wscript.Echo objDisk.DeviceID&quot;, &quot;Next&quot; };<br>
&nbsp;&nbsp; } else {<br>
&nbsp;&nbsp;&nbsp; scripts = new String[] { &quot;df | grep \&quot;^/\&quot; | awk '{print $1}'&quot; };<br>
&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; String[] strs = execute(scripts);<br>
&nbsp;&nbsp; if (strs == null) {<br>
&nbsp;&nbsp;&nbsp; logger.fine(&quot;not get disk information&quot;);<br>
&nbsp;&nbsp; } else {<br>
&nbsp;&nbsp;&nbsp; logger.fine(strs.length + &quot; disks:&quot;);<br>
&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; strs.length; i++) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; strs[i] = normalizeDiskName(strs[i]);<br>
&nbsp;&nbsp;&nbsp;&nbsp; logger.fine(&quot;disk &quot; + i + &quot;: &quot; + strs[i]);<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }<br>
&nbsp;&nbsp; return strs;<br>
 }</font></p>
<p><font color="#0000ff"> private HashMap&lt;String, Double&gt; getWinDSValues() {<br>
&nbsp;&nbsp; HashMap&lt;String, Double&gt; dsValues = new HashMap&lt;String, Double&gt;();</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; String[] scripts = new String[] {<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;strComputer = \&quot;.\&quot;&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;Set objWMIService = GetObject(\&quot;winmgmts:\&quot; _&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;&nbsp;&nbsp; &amp; \&quot;{impersonationLevel=impersonate}!\\\\\&quot; &amp; strComputer &amp; \&quot;</font><a href="file://root//cimv2/"><font color="#0000ff">\\root\\cimv2\</font></a><font color="#0000ff">&quot;)&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;set objRefresher = CreateObject(\&quot;WbemScripting.SWbemRefresher\&quot;)&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;Set colDisks = objRefresher.AddEnum(objWMIService, \&quot;Win32_PerfFormattedData_PerfDisk_LogicalDisk\&quot;).objectSet&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;objRefresher.Refresh&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;Wscript.Sleep 2000&quot;,</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp; &quot;objRefresher.Refresh&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;For Each objDisk in colDisks&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;&nbsp;&nbsp;&nbsp;  Wscript.Echo objDisk.Name &amp; \&quot; \&quot; &amp; objDisk.DiskReadBytesPerSec &amp; \&quot; \&quot; &amp; objDisk.DiskWriteBytesPerSec&quot;,<br>
&nbsp;&nbsp;&nbsp;&nbsp; &quot;Next&quot; };</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; String[] strs = execute(scripts);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; if (strs != null &amp;&amp; strs.length &gt; 0) {<br>
&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; strs.length; i++) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; String strValue = strs[i].trim();<br>
&nbsp;&nbsp;&nbsp;&nbsp; String[] values = strValue.split(&quot; &quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (values.length == 3) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String diskName = normalizeDiskName(values[0]);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long read = Long.parseLong(values[1]);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long write = Long.parseLong(values[2]);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dsValues.put(diskName + &quot;read&quot;, (double) read);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dsValues.put(diskName + &quot;write&quot;, (double) write);<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; return dsValues;<br>
 }</font></p>
<p><font color="#0000ff"> private HashMap&lt;String, Double&gt; getLinuxDSValues() {<br>
&nbsp;&nbsp; HashMap&lt;String, Double&gt; dsValues = new HashMap&lt;String, Double&gt;();</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; String[] scripts = new String[] { &quot;iostat -d -k 2 2&quot; };</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; String[] strs = execute(scripts);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; int count = 0;<br>
&nbsp;&nbsp; if (strs != null &amp;&amp; strs.length &gt; 0) {<br>
&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; strs.length; i++) {<br>
&nbsp;&nbsp;&nbsp;&nbsp; String strValue = replaceSpaces(strs[i].trim());<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (strValue.startsWith(&quot;Device:&quot;)) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp; if (count == 2) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] values = strValue.split(&quot; &quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (values.length == 6) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String diskName = normalizeDiskName(values[0]);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long read = (long) (Double.parseDouble(values[2]) * 1024);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long write = (long) (Double.parseDouble(values[3]) * 1024);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dsValues.put(diskName + &quot;read&quot;, (double) read);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dsValues.put(diskName + &quot;write&quot;, (double) write);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; return dsValues;<br>
 }</font></p>
<p><font color="#0000ff"> private String replaceSpaces(String str) {<br>
&nbsp;&nbsp; if (str == null)<br>
&nbsp;&nbsp;&nbsp; return &quot;&quot;;<br>
&nbsp;&nbsp; String str1;<br>
&nbsp;&nbsp; do {<br>
&nbsp;&nbsp;&nbsp; str1 = str;<br>
&nbsp;&nbsp;&nbsp; str = str.replace(&quot;\t&quot;, &quot; &quot;);<br>
&nbsp;&nbsp;&nbsp; str = str.replace(&quot;  &quot;, &quot; &quot;);<br>
&nbsp;&nbsp; } while (!str1.equals(str));</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; return str;<br>
 }</font></p>
<p><font color="#0000ff"> private String normalizeDiskName(String name) {<br>
&nbsp;&nbsp; String str = &quot;&quot;;</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; str = name.replace(&quot;:&quot;, &quot;&quot;);<br>
&nbsp;&nbsp; String[] strs = str.split(&quot;/&quot;);<br>
&nbsp;&nbsp; str = strs[strs.length - 1];</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; str = str.replaceAll(&quot;VolGroup&quot;, &quot;vg&quot;);<br>
&nbsp;&nbsp; str = str.replaceAll(&quot;LogVol&quot;, &quot;lv&quot;);</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; return str;<br>
 }</font></p>
<p><font color="#0000ff"> public static void main(String[] args) {<br>
&nbsp;&nbsp; DiskIoMonitor p = new DiskIoMonitor();<br>
&nbsp;&nbsp; p.start();</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; try {<br>
&nbsp;&nbsp;&nbsp; Thread.sleep(5 * 60 * 1000);<br>
&nbsp;&nbsp; } catch (InterruptedException e) {<br>
&nbsp;&nbsp;&nbsp; e.printStackTrace();<br>
&nbsp;&nbsp; }</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; p.stop();<br>
&nbsp;&nbsp; p.generateGraph();<br>
 }<br>
}<br>
</font></p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/5c7ff6ed157c16d3b31cb187.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/Java">Java</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/5c7ff6ed157c16d3b31cb187.html#comment">查看评论</a>]]></description>
        <pubDate>2009年01月11日 星期日  21:32</pubDate>
        <category><![CDATA[Java]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/5c7ff6ed157c16d3b31cb187.html</guid>
</item>

<item>
        <title><![CDATA[磁盘IO性能监控（Linux 和 Windows）]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/f4c5b0f30b7f56cc0b46e0ed.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">磁盘IO性能监控（Linux 和 Windows）</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>磁盘的IO性能是衡量计算机总体性能的一个重要指标。Linux提供了iostat命令来获却磁盘输入／输出（即IO）统计信息，Windows则提供了WMI接口，可以通过编写一个简单的脚本来获取与iostat相当的功能。</p>
<p><strong>1、Linux下的iostat命令</strong></p>
<p>iostat -d -k -t 2</p>
<p>每隔2秒统计一次磁盘IO信息，直到按Ctrl+C终止程序，-d 选项表示统计磁盘信息， -k 表示以每秒KB的形式显示，-t 要求打印出时间信息，2 表示每隔 2 秒输出一次。第一次输出的磁盘IO负载状况提供了关于自从系统启动以来的统计信息。随后的每一次输出则是每个间隔之间的平均IO负载状况。</p>
<p>运行该命令后，输出:</p>
<p>Linux 2.6.9-67.0.7.ELsmp (localhost.localdomain)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  11/19/2008</p>
<p>Time: 03:15:25 PM<br>
Device:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  tps&nbsp;&nbsp;&nbsp;  kB_read/s&nbsp;&nbsp;&nbsp;  kB_wrtn/s&nbsp;&nbsp;&nbsp;  kB_read&nbsp;&nbsp;&nbsp;  kB_wrtn<br>
sda&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  3.53&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  26.66&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  54.76&nbsp;&nbsp;  30122033&nbsp;&nbsp;  61864280<br>
sda1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  1.07&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  1.73&nbsp;&nbsp;&nbsp;  1207649&nbsp;&nbsp;&nbsp;  1949740<br>
sda2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  538&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  256<br>
sda3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  13.84&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  25.59&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  53.03&nbsp;&nbsp;  28913291&nbsp;&nbsp;  59914092</p>
<p>Time: 03:15:27 PM<br>
Device:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  tps&nbsp;&nbsp;&nbsp;  kB_read/s&nbsp;&nbsp;&nbsp;  kB_wrtn/s&nbsp;&nbsp;&nbsp;  kB_read&nbsp;&nbsp;&nbsp;  kB_wrtn<br>
sda&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  275.38&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  1738.69&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  3460<br>
sda1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  14.57&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  58.29&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  116<br>
sda2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0<br>
sda3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  419.60&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0.00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  1678.39&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  3340</p>
<p>...</p>
<p>每次输出都会打印时间信息, 接下来显示磁盘IO情况列表。</p>
<p>Device: 显示磁盘名称<br>
tps: 表示每秒钟输出到物理磁盘的传输次数。一次传输就是一个对物理磁盘的 I/O 请求。多个逻辑请求可被并为对磁盘的一个单一 I／O 请求。传输具有中等的大小。<br>
kB_read/s: 每秒从磁盘读取的数据量，单位为KB。<br>
kB_wrtn/s: 每秒从写入磁盘的数据量，单位为KB。<br>
Kb_read: 读取的 KB 总数。 <br>
Kb_wrtn: 写入的 KB 总数。</p>
<p><strong>2、WMI中的 Win32_PerfFormattedData_PerfDisk_LogicalDisk 对象</strong></p>
<p>Win32_PerfFormattedData_PerfDisk_LogicalDisk 代表逻辑磁盘性能数据对象，利用该对象可以获得磁盘的心能信息。Win32_PerfFormattedData_PerfDisk_LogicalDisk对象有以下一些主要的属性：</p>
<p>Name： 磁盘名称<br>
DiskTransfersPerSec：每秒磁盘传输次数。<br>
DiskReadBytesPerSec：每秒从磁盘读取得数据量，单位为Byte。<br>
DiskWriteBytesPerSec：每秒从磁盘读取得数据量，单位为Byte。<br>
PercentFreeSpace：可用磁盘百分比。</p>
<p><strong>3、使用 Win32_PerfFormattedData_PerfDisk_LogicalDisk 的注意事项</strong></p>
<p>在使用 Win32_PerfFormattedData_PerfDisk_LogicalDisk 时，需要注意：</p>
<p>（1）不能使用 objWMIService.ExecQuery 执行 Select 语句来获取磁盘性能数据<br>
（2）必须使用 WbemScripting.SWbemRefresher 将 Win32_PerfFormattedData_PerfDisk_LogicalDisk 加入，然后不断调用 Refresh 方法刷新数据来获取性能信息<br>
（3）第一次刷新的时候，并不能获取有用的数据，从第二次开始，才能获取到磁盘性能数据<br>
（4）以上问题与 WMI 中性能监控使用计数器的机制有关</p>
<p><strong>4、使用举例</strong></p>
<p>为了对监控磁盘性能提供一个良好的用户界面，可以利用VBScript编写脚本来获取磁盘性能数据。脚本的代码如下：</p>
<p><font color="#0000ff">'Script File Name: DiskMonitor.vbs</font></p>
<p><font color="#0000ff">strComputer = &quot;.&quot;<br>
Set objWMIService = GetObject(&quot;winmgmts:&quot; _<br>
&nbsp;&nbsp; &amp; &quot;{impersonationLevel=impersonate}!\\&quot; &amp; strComputer &amp; &quot;\root\cimv2&quot;)<br>
set objRefresher = CreateObject(&quot;WbemScripting.SWbemRefresher&quot;)<br>
Set colDisks = objRefresher.AddEnum(objWMIService, &quot;Win32_PerfFormattedData_PerfDisk_LogicalDisk&quot;).objectSet</font></p>
<p><font color="#0000ff">If Wscript.Arguments.Count = 0 Then<br>
 objRefresher.Refresh<br>
 For Each objDisk in colDisks<br>
&nbsp;&nbsp; Wscript.Echo objDisk.Name &amp; &quot; &quot; &amp; objDisk.DiskReadBytesPerSec &amp; &quot; &quot; &amp; objDisk.DiskWriteBytesPerSec<br>
 Next<br>
End If</font></p>
<p><font color="#0000ff">If Wscript.Arguments.Count = 1 Then<br>
 Interval = CInt(Wscript.Arguments(0)) * 1000<br>
 Do While True<br>
&nbsp;&nbsp; objRefresher.Refresh</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; Wscript.Echo<br>
&nbsp;&nbsp; Wscript.Echo &quot;Time: &quot; &amp; &quot; &quot; &amp; Time()<br>
&nbsp;&nbsp; Wscript.Echo FormatStr(&quot;Device:&quot;, 15, 0) &amp;  FormatStr(&quot;tps&quot;, 7, 1) &amp;  FormatStr(&quot;&nbsp;&nbsp;&nbsp;  kB_read/s&quot;, 13, 1)  &amp;  FormatStr(&quot;kB_wrtn/s&quot;, 13, 1) &amp;  FormatStr(&quot;Free Space&quot;, 13, 1)</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; For Each objDisk in colDisks<br>
&nbsp;&nbsp;&nbsp; Wscript.Echo FormatStr(objDisk.Name, 15, 0) &amp;  FormatStr(objDisk.DiskTransfersPerSec, 7, 1) &amp;  FormatStr(objDisk.DiskReadBytesPerSec, 13, 1)  &amp;  FormatStr(objDisk.DiskWriteBytesPerSec, 13, 1) &amp; FormatStr(objDisk.PercentFreeSpace &amp; &quot;%&quot;, 13, 1)<br>
&nbsp;&nbsp; Next<br>
&nbsp;&nbsp; Wscript.Sleep Interval<br>
 Loop<br>
End If</font></p>
<p><font color="#0000ff">If Wscript.Arguments.Count = 2 Then<br>
 i = 0<br>
 Interval = CInt(Wscript.Arguments(0)) * 1000<br>
 Count = CInt(Wscript.Arguments(1))<br>
 Do While i &lt; Count<br>
&nbsp;&nbsp; objRefresher.Refresh</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; Wscript.Echo<br>
&nbsp;&nbsp; Wscript.Echo &quot;Time: &quot; &amp; &quot; &quot; &amp; Time()<br>
&nbsp;&nbsp; Wscript.Echo FormatStr(&quot;Device:&quot;, 15, 0) &amp;  FormatStr(&quot;tps&quot;, 7, 1) &amp;  FormatStr(&quot;&nbsp;&nbsp;&nbsp;  kB_read/s&quot;, 13, 1)  &amp;  FormatStr(&quot;kB_wrtn/s&quot;, 13, 1) &amp;  FormatStr(&quot;Free Space&quot;, 13, 1)</font></p>
<p><font color="#0000ff">&nbsp;&nbsp; For Each objDisk in colDisks<br>
&nbsp;&nbsp;&nbsp; Wscript.Echo FormatStr(objDisk.Name, 15, 0) &amp;  FormatStr(objDisk.DiskTransfersPerSec, 7, 1) &amp;  FormatStr(objDisk.DiskReadBytesPerSec, 13, 1)  &amp;  FormatStr(objDisk.DiskWriteBytesPerSec, 13, 1) &amp; FormatStr(objDisk.PercentFreeSpace &amp; &quot;%&quot;, 13, 1)<br>
&nbsp;&nbsp; Next<br>
&nbsp;&nbsp; Wscript.Sleep Interval<br>
&nbsp;&nbsp; i = i + 1<br>
 Loop<br>
End If</font></p>
<p><font color="#0000ff">Function FormatStr(str, tLen, direction)<br>
 sLen = Len(str)<br>
 fStr = &quot;&quot;<br>
 num = tLen - sLen</font></p>
<p><font color="#0000ff"> j = 0<br>
 Do While j &lt; num<br>
&nbsp;&nbsp; fStr = fStr &amp; &quot; &quot;<br>
&nbsp;&nbsp; j = j + 1<br>
 Loop<br>
 <br>
 If direction = 1 Then<br>
&nbsp;&nbsp; fStr = fStr &amp; str<br>
 Else<br>
&nbsp;&nbsp; fStr = str &amp; fStr<br>
 End If<br>
 FormatStr = fStr<br>
End Function</font></p>
<p><br>
使用举例：</p>
<p>（1）CSCript DiskMonitor.vbs<br>
止刷新一次 Win32_PerfFormattedData_PerfDisk_LogicalDisk  对象，不会获取到有用的数据。</p>
<p>（2）CSCript DiskMonitor.vbs 2<br>
每隔 2 秒获取一次磁盘性能数据并输出，直到按 Ctrl+C 终止程序。</p>
<p>（3）CSCript DiskMonitor.vbs 2 100<br>
每隔 2 秒获取一次磁盘性能数据并输出，总共获取 100 次，然后退出。</p>
<p>该脚本输出的信息包括 DiskTransfersPerSec、DiskReadBytesPerSec、DiskWriteBytesPerSec 和 PercentFreeSpace。</p>
<p> </p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/f4c5b0f30b7f56cc0b46e0ed.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/Java">Java</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/f4c5b0f30b7f56cc0b46e0ed.html#comment">查看评论</a>]]></description>
        <pubDate>2008年11月19日 星期三  16:58</pubDate>
        <category><![CDATA[Java]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/f4c5b0f30b7f56cc0b46e0ed.html</guid>
</item>

<item>
        <title><![CDATA[Windows 动态链接库编程]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/70efd760e4b2ae43eaf8f831.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">Windows 动态链接库编程</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p><strong>1、介绍</strong></p>
<p>Windows操作系统是应用最关的操作系统，因此动态链接库也为程序员所熟悉，即使对于普通的使用者来说，很多时候也会碰到.dll结尾的文件，这就是动态链接库文件。Windows下的动态链接库可以通过参考头文件和.lib库文件进行编译，从而使得动态链接库隐式地被使用；也可以使用LoadLibrary、GetProcAddress等函数来显式调用动态链接库。</p>
<p><strong>2、语法、导入导出</strong></p>
<p>在Windows编程中，对于要使用或被使用的函数或者变量，需要使用 __declspec 关键字来声明，以告诉编译器该变量或函数不是普通的变量或函数，而是一个动态链接库的接口属性。</p>
<p>如果定义一个要被其他代码使用的函数，可以写成：<br>
 __declspec( dllexport )  int add(int a, int b)；</p>
<p>如果在该代码中，打算使用另外一个程序中的变量，则可以写成：<br>
 __declspec( dllimport )  char *name；</p>
<p>动态链接库通常包含一系列供其他程序使用函数，因此 declspec( dllexport ) 语法形式最为常用。如果动态库需要其他程序中的定义的全局变量，则需要在其他程序中使用导出该变量，在动态链接库中则需要使用 extern declspec( dllexport ) 将该变量声明为外部变量以便使用。</p>
<p><strong>3、链接方式</strong></p>
<p>可以以下列两种方式之一链接到（或加载）DLL：</p>
<p> 隐式链接<br>
 显式链接</p>
<p>隐式链接有时称为静态加载或加载时动态链接。显式链接有时称为动态加载或运行时动态链接。在隐式链接下，使用 DLL 的可执行文件链接到该 DLL 的创建者所提供的导入库（.lib 文件）。使用 DLL 的可执行文件加载时，操作系统加载此 DLL。客户端可执行文件调用 DLL 的导出函数，就好像这些函数包含在可执行文件内一样。</p>
<p>在显式链接下，使用 DLL 的可执行文件必须进行函数调用以显式加载和卸载该 DLL，并访问该 DLL 的导出函数。客户端可执行文件必须通过函数指针调用导出函数。可执行文件对两种链接方法可以使用同一个 DLL。另外，由于一个可执行文件可隐式链接到某个 DLL，而另一个可显式附加到此 DLL，故这些机制不是互斥的。</p>
<p><strong>4、隐式链接</strong></p>
<p>隐式链接动态链接库比较简单，不予详述。</p>
<p><strong>5、显式链接API函数</strong></p>
<p>显式链接主要涉及到3个API函数（ LoadLibrary , GetProcAddress 和 FreeLibrary ），要使用这些函数包含windows.h头文件即可。</p>
<p>（1）HINSTANCE LoadLibrary(LPCSTR lpLibFileName);</p>
<p>该函数用来加载指定动态库文件，并且返回句柄。</p>
<p>参数lpLibFileName为动态链接库的名称。Windows 首先搜索&ldquo;已知 DLL&rdquo;，如 Kernel32.dll 和 User32.dll。然后按下列顺序搜索 DLL：</p>
<p>1、当前进程的可执行模块所在的目录。<br>
2、当前目录。<br>
3、Windows 系统目录。GetSystemDirectory 函数检索此目录的路径。<br>
4、Windows 目录。GetWindowsDirectory 函数检索此目录的路径。<br>
5、PATH 环境变量中列出的目录。</p>
<p>（2）FARPROC GetProcAddress (HMODULE hModule, LPCSTR lpProcName);</p>
<p>函数GetProcAddress 用来获取 DLL 导出函数的地址。返回由lpProcName指定的变量或函数指针。</p>
<p>参数hModule为已经加载的动态库文件的句柄。</p>
<p>参数lpProcName为要调用的变量或函数名称。</p>
<p>（3）BOOL FreeLibrary(HMODULE hModule);<br>
从内存中释放hModule所代表的动态链接库。</p>
<p>（4）如果发生错误，可以调用GetLastError()函数或去错误代码。</p>
<p><strong>6、显示链接举例</strong></p>
<p>（1）动态库文件代码：dll_demo.c</p>
<p>#include &lt;stdio.h&gt;</p>
<p>__declspec( dllexport )  int add(int a, int b)<br>
{<br>
 printf(&quot;calling add\n&quot;);<br>
 return a + b;<br>
}</p>
<p>该文件中的add()函数计算两个整数之和，并且返回之前打印提示字符串。函数使用 __declspec( dllexport ) 语法来说明函数add(int a, int b)要被导出。</p>
<p>（2）客户端事例代码：main.c</p>
<p>#include &lt;stdio.h&gt;<br>
#include &lt;windows.h&gt;</p>
<p>int main (int argc, char *argv[])<br>
{<br>
 int a = 10, b = 20;<br>
 int c = 0;<br>
 HINSTANCE&nbsp;&nbsp;  hInstLibrary&nbsp;&nbsp;  =&nbsp;&nbsp;  NULL;<br>
 int (*add)();</p>
<p> printf (&quot;Load DLL file\n&quot;);<br>
 if ((hInstLibrary = LoadLibrary(L&quot;dll_demo.dll&quot;)) == NULL)<br>
 {<br>
&nbsp;&nbsp; printf (&quot;***LoadLibrary ERROR: %d.\n&quot;, GetLastError());<br>
&nbsp;&nbsp; return 1;<br>
 }<br>
 if((add = (int (*)())GetProcAddress(hInstLibrary, &quot;add&quot;)) == NULL) {<br>
&nbsp;&nbsp; printf (&quot;***GetProcAddress ERROR: %d.\n&quot;, GetLastError());<br>
&nbsp;&nbsp; return 1;<br>
 } </p>
<p> c = add(a, b);<br>
 printf(&quot;%d + %d = %d\n&quot;, a, b, c);</p>
<p> FreeLibrary(hInstLibrary);<br>
 return 0;<br>
}</p>
<p>程序利用LoadLibrary函数加载动态链接dll_demo.dll，利用FreeLibrary关闭句柄，利用GetLastError()获取错误代码，利用GetProcAddress定位共享库中的add函数，然后调用该函数执行加法计算，并打印结果。</p>
<p>（3）编译与运行</p>
<p>编译共享库：</p>
<p>在VS.Net中创建一个动态链接库项目，名称为dll_demo，加入文件dll_demo.c，编译后生成dll_demo.dll文件。</p>
<p>编译事例程序：</p>
<p>在VS.Net中创建一个动态链接库项目，名称为dll_main，加入文件main.c，编译后生成dll_main.exe可以执行文件。</p>
<p>运行：</p>
<p>将 dll_demo.dll 和 dll_main.exe 放在同一个目录下，然后双击运行 dll_main.exe。</p>
<p>输出：</p>
<p>Load DLL file<br>
calling add<br>
10 + 20 = 30</p>
<p><strong>7、调用动态链接库中的变量</strong></p>
<p>也可以使用动态链接库中的变量。例如，在动态链接库中定义：</p>
<p>__declspec( dllexport )  int num = 100;</p>
<p>那么可以在事例程序中这样调用：</p>
<p>int *d;<br>
d = (int *)GetProcAddress(hInstLibrary, &quot;num&quot;);<br>
printf(&quot;num = %d\n&quot;, *d);<br>
&nbsp;&nbsp;&nbsp;</p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/70efd760e4b2ae43eaf8f831.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/c%D3%EF%D1%D4">c语言</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/70efd760e4b2ae43eaf8f831.html#comment">查看评论</a>]]></description>
        <pubDate>2008年11月17日 星期一  15:10</pubDate>
        <category><![CDATA[c语言]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/70efd760e4b2ae43eaf8f831.html</guid>
</item>

<item>
        <title><![CDATA[Linux 共享库编程]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/1d9bf3c2e13a9f32e4dd3b4f.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">Linux 共享库编程</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p><strong>1、介绍</strong></p>
<p>动态库是程序设计常用的技术，采用动态库可以有效的减少程序大小，节省空间，提高效率，增加程序的可扩展性，便于模块化管理。在Windows和Linux操作系统中都有动态库的概念。Windows将其称为动态链接库（Dynamic Link Library，DLL），其文件扩展名为.dll，Linux称其为共享库技术（Shared Library），相应的共享库文件扩展名为.so。</p>
<p>故名思义，动态库在程序运行的时候被动态链接。但是在具体使用动态库的时候却有两种不同的方式：隐式链接和显式链接。隐式链接在编译/链接阶段完成，由编译系统根据动态库的头文件和库文件进行编译和链接，从而确定待调用的函数原形和地址。显式链接则是利用API函数实现加载和卸载共享库，获取带调用函数地址，获取错误信息等功能。</p>
<p><strong>2、隐式链接举例</strong></p>
<p>（1）动态库文件代码：dl_func.c</p>
<p>#include &lt;stdio.h&gt;<br>
extern char name[];<br>
int add(int a, int b)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  printf(&quot;calling add\n&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  printf(&quot;Hello, %s!\n&quot;, name);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return a + b;<br>
}</p>
<p>该文件中的add()函数计算两个整数之和，并且打印外部变量的值，该外部变量由调用共享库的事例程序定义。</p>
<p>（2）客户端事例代码：dl_demo1.c</p>
<p>#include &lt;stdio.h&gt;<br>
#include &lt;dlfcn.h&gt;</p>
<p>int add(int a, int b);</p>
<p>char name[100];<br>
int main(int argc, char *argv[]) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int a = 10, b = 20;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int c = 0;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  strcpy(name, &quot;NHN XDBMS&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  c = add(a, b);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  printf(&quot;%d + %d = %d\n&quot;, a, b, c);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return 0;<br>
}</p>
<p>该事例程序调用共享库的中的add()函数计算两数之后并打印，同时在事例程序中，给变量name赋值，以便在add()函数中打印。</p>
<p>（3）编译与运行</p>
<p>编译共享库：</p>
<p>gcc -o libdl_func.so -fPIC -rdynamic -shared dl_func.c<br>
选项-fPIC指示编译器将代码编译成位置独立的代码，一般需要以程序文件共享其函数或变量给其他程序文件的代码都应该以此选项进行编译，选项-rdynamic指示编译器所编译/链接的为共享库程序文件。由于要使用外部变量，因此需要-shared选项，否则编译器会抛出错误信息：undefined reference to `name'，表示不能找到name变量。</p>
<p>编译事例程序：</p>
<p>gcc -o dl_demo1 -L./ -ldl_func dl_demo1.c<br>
选项-L./ 指示编译器在当前目录下寻找共享库文件，-ldl_func指示需要的共享库文件名为libdl_func.so。</p>
<p>运行：</p>
<p>./dl_demo1</p>
<p>输出：</p>
<p>calling add<br>
Hello, NHN XDBMS!<br>
10 + 20 = 30</p>
<p><strong>3、显式链接API函数</strong></p>
<p>显式<strong>链接</strong>主要涉及到4个API函数（ dlopen , dlerror , dlsym 和 dlclose ），这些函数原形定义包含在dlfcn.h头文件中。</p>
<p>（1）void *dlopen(const char *file, int mode);</p>
<p>该函数用来按照指定模式打开指定的共享库，将其影射到内存中，并且返回句柄。<br>
第一个参数：指定共享库的名称，将会在下面位置查找指定的共享库。<br>
－环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录。<br>
－文件/etc/ld.so.cache中找到的库的列表，用ldconfig维护。<br>
－目录usr/lib。<br>
－目录/lib。<br>
－当前目录。<br>
第二个参数：指定如何打开共享库。<br>
－RTLD_NOW：将共享库中的所有函数加载到内存<br>
－RTLD_LAZY：会推后共享库中的函数的加载操作，直到调用dlsym()时方加载某函数</p>
<p>（2）void *dlsym(void *restrict handle, const char *restrict name);</p>
<p>该函数返回一个指向由name所确定的请求入口点的指针。调用dlsym时，利用dlopen()返回的共享库的phandle以及函数/变量名称作为参数，返回要加载函数/变量的入口地址。</p>
<p>（3）char *dlerror(void);</p>
<p>dlerror 返回 NULL 或者一个指向描述最近错误的 ASCII 字符串的指针</p>
<p>（4）int dlclose(void *handle);</p>
<p>关闭句柄并且取消共享目标文件的映射&nbsp;&nbsp;</p>
<p><strong>4、显式链接举例</strong></p>
<p>（1）动态库文件代码：dl_func.c</p>
<p>与隐式链接的代码相同。</p>
<p>（2）客户端事例代码：dl_demo.c</p>
<p>#include &lt;stdio.h&gt;<br>
#include &lt;dlfcn.h&gt;</p>
<p>char name[100];<br>
int main(int argc, char *argv[]) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int a = 10, b = 20;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int c = 0;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  void *dlh = NULL;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  int (*add)();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  strcpy(name, &quot;NHN XDBMS&quot;);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if((dlh = dlopen(&quot;libdl_func.so&quot;, RTLD_LAZY)) == NULL) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  fprintf (stderr, &quot;***DL ERROR: %s.\n&quot;, dlerror ());<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return 1;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if((add = (int (*)())dlsym(dlh, &quot;add&quot;)) == NULL) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  fprintf (stderr, &quot;***DL ERROR: %s.\n&quot;, dlerror ());<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return 1;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  c = add(a, b);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  printf(&quot;%d + %d = %d\n&quot;, a, b, c);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  dlclose(dlh);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return 0;<br>
}</p>
<p>该事例程序给变量name赋值，以便在add()函数中打印。程序利用dlopen函数加载共享库libdl_func.so，利用dlclose关闭句柄，利用dlerror获取错误信息，利用dlsym定位共享库中的add函数，然后调用该函数执行加法计算。</p>
<p>（3）编译与运行</p>
<p>编译共享库：</p>
<p>与前述共享库编译方法相同。</p>
<p>编译事例程序：</p>
<p>gcc -o dl_demo -fPIC -ldl dl_demo.c<br>
由于变量name需要被共享库中的add()函数使用，因此必须使用选项-fPIC。选项-ldl指示编译器需要用来到libdl.so库文件。</p>
<p>运行：</p>
<p>./dl_demo</p>
<p>输出：</p>
<p>与隐式链接事例的输出相同。</p>
<p><strong>5、其他</strong></p>
<p>（1）如事例中所给出的，除了共享库可以给别人使用外，共享库也可以使用调用程序中的变量，如在共享库中打印事例程序中的name。不过由于name在外部定义和声明因此在链接共享库时需要使用-shared选项。</p>
<p>（2）除了可以共享函数外，还可以共享变量，如果在dl_func.c中定义个变量:</p>
<p>int num = 100;</p>
<p>那么可以在事例程序中这样调用：</p>
<p>int *d;<br>
d = (int *)dlsym(dlh, &quot;num&quot;);<br>
printf(&quot;num = %d\n&quot;, *d);<br>
&nbsp;&nbsp;&nbsp;&nbsp;</p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/1d9bf3c2e13a9f32e4dd3b4f.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/c%D3%EF%D1%D4">c语言</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/1d9bf3c2e13a9f32e4dd3b4f.html#comment">查看评论</a>]]></description>
        <pubDate>2008年11月10日 星期一  15:07</pubDate>
        <category><![CDATA[c语言]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/1d9bf3c2e13a9f32e4dd3b4f.html</guid>
</item>

<item>
        <title><![CDATA[访问量突破10000，发贴庆祝]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/70efd76010487242eaf8f82a.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">访问量突破10000，发贴庆祝</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>自从春节过后在在此开博至今，其间时而勤快，时而懒散，时至今日累计访问量终于突破10000大关，特此发贴庆祝。</p>
<p> </p>
<div forimg="1"><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/8fe861545bc6cc43d009060b.jpg"></div> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/70efd76010487242eaf8f82a.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/%CF%D0%BB%B0%D0%C4%C7%E9">闲话心情</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/70efd76010487242eaf8f82a.html#comment">查看评论</a>]]></description>
        <pubDate>2008年10月24日 星期五  21:26</pubDate>
        <category><![CDATA[闲话心情]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/70efd76010487242eaf8f82a.html</guid>
</item>

<item>
        <title><![CDATA[使用RowSet]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/52f43b9460aaa818d31b705f.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">使用RowSet</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>ResultSet是使用Jdbc编程的人入门和常用的操作数据库的类，自 JDK 1.4 开始，易于使用RowSet接口被引入。RowSet 接口扩展了标准 java.sql.ResultSet 接口。RowSetMetaData 接口扩展了 java.sql.ResultSetMetaData 接口。因此，熟悉 JDBC API 的开发人员必须学习少数几个新 API 才能使用 rowset。此外，与 JDBC ResultSet 对象配套使用的第三方软件工具也可以方便地用于 rowset。但是在JDK 1.4中，只有一个RowSet接口，使得RowSet的使用范围打了折扣。不过 JDK 5.0 定义了5 个标准的 JDBC RowSet 接口，并且给出了相应的参考实现，因此可以很方便的使用RowSet接口所提供的功能。</p>
<p>RowSet 对象可以建立一个与数据源的连接并在其整个生命周期中维持该连接，在此情况下，该对象被称为连接的 rowset。rowset 还可以建立一个与数据源的连接，从其获取数据，然后关闭它。这种 rowset 被称为非连接 rowset。非连接 rowset 可以在断开时更改其数据，然后将这些更改发送回原始数据源，不过它必须重新建立连接才能完成此操作。 相比较 java.sql.ResultSet 而言，RowSet 的离线操作能够有效的利用计算机越来越充足的内存，减轻数据库服务器的负担，由于数据操作都是在内存中进行然后批量提交到数据源，灵活性和性能都有了很大的提高。RowSet 默认是一个可滚动，可更新，可序列化的结果集，而且它作为 JavaBeans，可以方便地在网络间传输，用于两端的数据同步。</p>
<p><strong>1、与ResultSet比较</strong></p>
<p>（1）RowSet扩展了ResultSet接口，因此可以像使用ResultSet一样使用RowSet。</p>
<p>（2）RowSet扩展了ResultSet接口，因此功能比ResultSet更多、更丰富。</p>
<p>（3）默认情况下，所有 RowSet 对象都是可滚动的和可更新的。而ResultSet是只能向前滚动和只读的。</p>
<p>（4）RowSet可以是非链接的，而ResultSet是连接的。因此利用CacheRowSet接口可以离线操作数据。</p>
<p>（5）RowSet接口添加了对 JavaBeans 组件模型的 JDBC API 支持。rowset 可用作可视化 Bean 开发环境中的 JavaBeans 组件。</p>
<p>（6）RowSet采用了新的连接数据库的方法。</p>
<p>（7）CacheRowSet是可以序列化的。</p>
<p>（8）RowSet和ResultSet都代表一行行的数据、属性以及相关操作方法。</p>
<p>（9）自己认为，应该倾向于把RowSet看成是与数据库无关的东西，它只是一个代表一行行数据的对象，而ResultSet则是一个与数据库紧密联系的东西。</p>
<p><strong>2、JDK 5.0 的5个标准RowSet接口</strong></p>
<p>在JDK 5.0中，5个标准RowSet接口包括 CachedRowSet，WebRowSet，FilteredRowSet，JoinRowSet 和 JdbcRowSet。相应的参考实现是Sun公司给出的，位于com.sun.rowset包下，分别为为CachedRowSetImpl，WebRowSetImpl，FilteredRowSetImpl，JoinRowSetImpl 和 JdbcRowSetImpl。这5个标准接口中JdbcRowSet是链接的rowset，而其他4个是非链接的rowset。</p>
<p>（1）CachedRowSet：最常用的一种 RowSet。其他三种 RowSet（WebRowSet，FilteredRowSet，JoinRowSet）都是直接或间接继承于它并进行了扩展。它提供了对数据库的离线操作，可以将数据读取到内存中进行增删改查，再同步到数据源。CachedRowSet是可滚动的、可更新的、可序列化，可作为 JavaBeans 在网络间传输。支持事件监听，分页等特性。 CachedRowSet 对象通常包含取自结果集的多个行，但是也可包含任何取自表格式文件（如电子表格）的行。</p>
<p>（2）WebRowSet：继承自 CachedRowSet，并可以将 WebRowSet 写到 XML 文件中，也可以用符合规范的 XML 文件来填充 WebRowSet。</p>
<p>（3）FilteredRowSet：通过设置 Predicate（在 javax.sql.rowset 包中），提供数据过滤的功能。可以根据不同的条件对 RowSet 中的数据进行筛选和过滤。</p>
<p>（4）JoinRowSet：提供类似 SQL JOIN 的功能，将不同的 RowSet 中的数据组合起来。目前在 Java 6 中只支持内联（Inner Join）。</p>
<p>（5）JdbcRowSet：对 ResultSet 的一个封装，使其能够作为 JavaBeans 被使用，是唯一一个保持数据库连接的 RowSet。JdbcRowSet 对象是连接的 RowSet 对象，也就是说，它必须使用启用 JDBC 技术的驱动程序（&ldquo;JDBC 驱动程序&rdquo;）来持续维持它与数据源的连接。</p>
<p><strong>3、填充RowSet</strong></p>
<p>前面说过，应该倾向于把RowSet看成是与数据库无关而只代表一行行数据的对象，因此就涉及到数据从哪里来的问题。</p>
<p>（1）从数据库直接获取数据</p>
<p>由于大部分情况下，与数据打交道也就是与数据库打交道，因此RowSet接口提供了通过JDBC直接从数据库获取数据的方法，以参考实现JdbcRowSetImpl为例，就是这样：</p>
<p><font color="#0000ff">&nbsp;&nbsp;  RowSet rs = new JdbcRowSetImpl(); //也可以是CachedRowSetImpl，WebRowSetImpl，FilteredRowSetImpl，JoinRowSetImpl。<br>
&nbsp;&nbsp;  rs.setUrl(&quot;jdbc:mysql:///test&quot;);<br>
&nbsp;&nbsp;  rs.setUsername(&quot;root&quot;);<br>
&nbsp;&nbsp;  rs.setPassword(&quot;&quot;);<br>
&nbsp;&nbsp;  rs.setCommand(&quot;SELECT * FROM EMPLOYEES&quot;);<br>
&nbsp;&nbsp;  rs.execute();</font></p>
<p>设置好相关属性，运行execute()方法后，EMPLOYEES表中的数据就被填充到rs对象中了。<br>
除了通过设置JDBC连接URL、用户名和密码外，RowSet也可以使用数据源名称属性的值来查找已经在命名服务中注册的 DataSource 对象。完成检索后，可以使用 DataSource 对象创建到它所表示的数据源的连接，设置数据源名称可以使用setDataSourceName()方法。</p>
<p>（2）用ResultSet填充</p>
<p>在有现成ResultSet的情况下，如果想将其作为RowSet使用；或者当 DBMS 不提供对滚动和更新的完全支持时，如果想使不可滚动和只读的 ResultSet 对象变得可滚动和可更新，可以创建一个使用该 ResultSet 对象的数据所填充的 CachedRowSet 对象。</p>
<p><font color="#0000ff">  ResultSet rs = stmt.executeQuery(&quot;SELECT * FROM EMPLOYEES&quot;);<br>
&nbsp;&nbsp;  CachedRowSet crs = new CachedRowSetImpl(); //也可以是WebRowSetImpl，FilteredRowSetImpl，JoinRowSetImpl，因为他们均继承自CachedRowSetImpl<br>
&nbsp;&nbsp;  crs.populate(rs);</font></p>
<p>运行populate()方法后，ResultSet对象rs中的数据就被填充到crs对象中了。</p>
<p>（3）用XML填充</p>
<p>如果您打算将XML作为数据交换格式在客户端和你的服务器之间传输数据并且向实现数据离线编辑、或者向使用XML格式的数据的话，可以使用WebRowSet接口来用XML填充数据。</p>
<p><font color="#0000ff">&nbsp;&nbsp;  WebRowSet wrs = new WebRowSetImpl(); <br>
&nbsp;&nbsp;  wrs.readXml(new FileReader(new File(&quot;D:\\employees.xml&quot;)));</font></p>
<p>运行readXml()方法后，employees.xml文件的数据就被填充到wrs对象中了。employees.xml 文件的格式参见附录。</p>
<p>（4）用其他方法填充</p>
<p>如果形用其他方式填充，比如csv、excel、text、http等格式或方法填充数据，那么就需要自己编写代码实现RowSet。</p>
<p><strong>4、操作RowSet中的数据及元数据</strong></p>
<p>除了ResultSet提供的操作数据和元数据方法外，RowSet接口没有提供太多额外的方法。</p>
<p>1）更新数据<br>
<font color="#0000ff">&nbsp;&nbsp;  rs.absolute(5);<br>
&nbsp;&nbsp;  rs.updateInt(1, 10);<br>
&nbsp;&nbsp;  rs.updateInt(2, 1000);<br>
&nbsp;&nbsp;  rs.updateString(3, &quot;John&quot;);<br>
&nbsp;&nbsp;  rs.updateRow();</font></p>
<p><font color="#0000ff"><br>
</font>（2）插入数据<br>
<font color="#0000ff">&nbsp;&nbsp;  rs.moveToInsertRow();<br>
&nbsp;&nbsp;  rs.updateInt(1, 10);<br>
&nbsp;&nbsp;  rs.updateInt(2, 1000);<br>
&nbsp;&nbsp;  rs.updateString(3, &quot;John&quot;);<br>
&nbsp;&nbsp;  rs.insertRow();</font></p>
<p><font color="#0000ff"><br>
</font>（3）删除数据<br>
<font color="#0000ff">&nbsp;&nbsp;  rs.absolute(5);<br>
&nbsp;&nbsp;  rs.deleteRow();</font></p>
<p><font color="#0000ff"><br>
</font>（4）设置属性<br>
<font color="#0000ff">&nbsp;&nbsp;  rs.setCommand(&quot;select id, salary, name from employees where id = ?&quot;);<br>
&nbsp;&nbsp;  rs.setInt(1, 1);<br>
&nbsp;&nbsp;  rs.execute();</font></p>
<p><font color="#0000ff"><br>
</font>（5）元数据<br>
<font color="#0000ff">&nbsp;&nbsp;  RowSetMetaData rsmd = (RowSetMetaData)rs.getMetaData();<br>
&nbsp;&nbsp;  int count = rsmd.getColumnCount();<br>
&nbsp;&nbsp;  int type = rsmd.getColumnType(2);<br>
</font>&nbsp;&nbsp;&nbsp;  <br>
<strong>5、事务与更新底层数据源</strong></p>
<p>RowSet本身只代表具体数据，事务以及底层数据源的更新是与底层数据源密切相关的概念。对于JDBC数据源，相应的标准接口JdbcRowSet通过与数据库相关的方法来来实现，如commit()，rollback()等。对于标准接口的中非连接rowset，如CachedRowSet，则在对RowSet中的数据改动后，通过运行acceptChanges()方法，在内部调用 RowSet 对象的 writer 将这些更改写入数据源，从而将 CachedRowSet 对象中的更改传播回底层数据源。</p>
<p><strong>6、可序列化非连接RowSet</strong></p>
<p>使用 CachedRowSet 对象的主要原因之一是要在应用程序的不同组件之间传递数据。因为 CachedRowSet 对象是可序列化的，所以可使用它（举例来说）将运行于服务器环境的企业 JavaBeans 组件执行查询的结果通过网络发送到运行于 web 浏览器的客户端。</p>
<p>由于 CachedRowSet 对象是非连接的，所以和具有相同数据的 ResultSet 对象相比更为简洁。因此，它特别适于向瘦客户端（如 PDA）发送数据，这种瘦客户端由于资源限制或安全考虑而不适于使用 JDBC 驱动程序。所以 CachedRowSet 对象可提供一种&ldquo;获取各行&rdquo;的方式而无需实现全部 JDBC API。</p>
<p>ebRowSet继承自CachedRowSet，除了拥有CachedRowSet的优点外，还可以将WebRowSet输出成XML，也可以将XML转换成WebRowSet，更加适合在Web环境中使用。标准的 WebRowSet XML 模式定义位于 URI <a href="http://java.sun.com/xml/ns/jdbc/webrowset.xsd">http://java.sun.com/xml/ns/jdbc/webrowset.xsd</a>。将WebRowSet保存为XML的代码事例如下：</p>
<p><font color="#0000ff">&nbsp;&nbsp;  wrs.setCommand(&quot;select id, salary, name from employees&quot;);<br>
&nbsp;&nbsp;  wrs.execute();&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;  wrs.writeXml(new FileWriter(new File(&quot;D:\\employees.xml&quot;)));</font></p>
<p><strong>附：employees.xml</strong></p>
<p><font color="#0000ff">&lt;?xml version=&quot;1.0&quot;?&gt;<br>
&lt;webRowSet xmlns=&quot;</font><a href="http://java.sun.com/xml/ns/jdbc"><font color="#0000ff">http://java.sun.com/xml/ns/jdbc</font></a><font color="#0000ff">&quot; xmlns:xsi=&quot;</font><a href="http://www.w3.org/2001/XMLSchema-instance"><font color="#0000ff">http://www.w3.org/2001/XMLSchema-instance</font></a><font color="#0000ff">&quot;<br>
xsi:schemaLocation=&quot;</font><a href="http://java.sun.com/xml/ns/jdbc"><font color="#0000ff">http://java.sun.com/xml/ns/jdbc</font></a><font color="#0000ff"> </font><a href="http://java.sun.com/xml/ns/jdbc/webrowset.xsd"><font color="#0000ff">http://java.sun.com/xml/ns/jdbc/webrowset.xsd</font></a><font color="#0000ff">&quot;&gt;<br>
&lt;properties&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;command&gt;select id, salary, name from employees&lt;/command&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;concurrency&gt;1008&lt;/concurrency&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;datasource&gt;&lt;null/&gt;&lt;/datasource&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;escape-processing&gt;true&lt;/escape-processing&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;fetch-direction&gt;1000&lt;/fetch-direction&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;fetch-size&gt;0&lt;/fetch-size&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;isolation-level&gt;2&lt;/isolation-level&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;key-columns&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/key-columns&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;map&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/map&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;max-field-size&gt;0&lt;/max-field-size&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;max-rows&gt;0&lt;/max-rows&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;query-timeout&gt;0&lt;/query-timeout&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;read-only&gt;true&lt;/read-only&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;rowset-type&gt;ResultSet.TYPE_SCROLL_INSENSITIVE&lt;/rowset-type&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;show-deleted&gt;false&lt;/show-deleted&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;table-name&gt;employees&lt;/table-name&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;url&gt;jdbc:mysql:///test&lt;/url&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;sync-provider&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;sync-provider-name&gt;com.sun.rowset.providers.RIOptimisticProvider&lt;/sync-provider-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;sync-provider-vendor&gt;Sun Microsystems Inc.&lt;/sync-provider-vendor&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;sync-provider-version&gt;1.0&lt;/sync-provider-version&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;sync-provider-grade&gt;2&lt;/sync-provider-grade&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;data-source-lock&gt;1&lt;/data-source-lock&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/sync-provider&gt;<br>
&lt;/properties&gt;<br>
&lt;metadata&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;column-count&gt;3&lt;/column-count&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;column-definition&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-index&gt;1&lt;/column-index&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;auto-increment&gt;false&lt;/auto-increment&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;case-sensitive&gt;false&lt;/case-sensitive&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;currency&gt;false&lt;/currency&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;nullable&gt;0&lt;/nullable&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;signed&gt;true&lt;/signed&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;searchable&gt;true&lt;/searchable&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-display-size&gt;11&lt;/column-display-size&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-label&gt;id&lt;/column-label&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-name&gt;id&lt;/column-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;schema-name&gt;&lt;/schema-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-precision&gt;11&lt;/column-precision&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-scale&gt;0&lt;/column-scale&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;table-name&gt;employees&lt;/table-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;catalog-name&gt;test&lt;/catalog-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-type&gt;4&lt;/column-type&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-type-name&gt;INT&lt;/column-type-name&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/column-definition&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;column-definition&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-index&gt;2&lt;/column-index&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;auto-increment&gt;false&lt;/auto-increment&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;case-sensitive&gt;false&lt;/case-sensitive&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;currency&gt;false&lt;/currency&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;nullable&gt;1&lt;/nullable&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;signed&gt;true&lt;/signed&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;searchable&gt;true&lt;/searchable&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-display-size&gt;11&lt;/column-display-size&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-label&gt;salary&lt;/column-label&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-name&gt;salary&lt;/column-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;schema-name&gt;&lt;/schema-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-precision&gt;11&lt;/column-precision&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-scale&gt;0&lt;/column-scale&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;table-name&gt;employees&lt;/table-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;catalog-name&gt;test&lt;/catalog-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-type&gt;4&lt;/column-type&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-type-name&gt;INT&lt;/column-type-name&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/column-definition&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;column-definition&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-index&gt;3&lt;/column-index&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;auto-increment&gt;false&lt;/auto-increment&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;case-sensitive&gt;false&lt;/case-sensitive&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;currency&gt;false&lt;/currency&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;nullable&gt;1&lt;/nullable&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;signed&gt;false&lt;/signed&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;searchable&gt;true&lt;/searchable&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-display-size&gt;25&lt;/column-display-size&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-label&gt;name&lt;/column-label&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-name&gt;name&lt;/column-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;schema-name&gt;&lt;/schema-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-precision&gt;25&lt;/column-precision&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-scale&gt;0&lt;/column-scale&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;table-name&gt;employees&lt;/table-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;catalog-name&gt;test&lt;/catalog-name&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-type&gt;12&lt;/column-type&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;column-type-name&gt;VARCHAR&lt;/column-type-name&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/column-definition&gt;<br>
&lt;/metadata&gt;<br>
&lt;data&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;currentRow&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;columnValue&gt;1&lt;/columnValue&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;columnValue&gt;1000&lt;/columnValue&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;columnValue&gt;John&lt;/columnValue&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/currentRow&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;currentRow&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;columnValue&gt;2&lt;/columnValue&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;columnValue&gt;1200&lt;/columnValue&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;columnValue&gt;Tom&lt;/columnValue&gt;<br>
&nbsp;&nbsp;&nbsp;  &lt;/currentRow&gt;<br>
&lt;/data&gt;<br>
&lt;/webRowSet&gt;<br>
</font></p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/52f43b9460aaa818d31b705f.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/Java">Java</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/52f43b9460aaa818d31b705f.html#comment">查看评论</a>]]></description>
        <pubDate>2008年10月10日 星期五  11:21</pubDate>
        <category><![CDATA[Java]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/52f43b9460aaa818d31b705f.html</guid>
</item>

<item>
        <title><![CDATA[奶奶给宝宝做的鞋]]></title>
        <link><![CDATA[http://hi.baidu.com/li%5Fzhongnan/blog/item/9f13003e19b6fdfc838b1332.html]]></link>
        <description><![CDATA[
		
		<p><strong><font size="4">奶奶给宝宝做的鞋</font></strong></p>
<p> </p>
<strong>作者：终南&nbsp;&nbsp;  &lt;<a href="mailto:li.zhongnan@hotmail.com">li.zhongnan@hotmail.com</a>&gt;</strong>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>1、我家宝宝</p>
<p><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/2dafbf4f59796a2caec3ab14.jpg"></p>
<p>2、小猪鞋</p>
<p><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/fd00b6469d9223136a63e510.jpg"></p>
<p>3、公鸡鞋</p>
<p><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/13016dc84214540d7e3e6f1d.jpg"></p>
<p>4、老虎鞋</p>
<p><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/e41b4b03c8143df609fa93e6.jpg"></p>
<p>5、小猫鞋</p>
<p><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/2b806d136a6f1f185baf53e1.jpg"></p>
<p>6、小鸟鞋</p>
<p><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/369a4419b1baab60dbb4bded.jpg"></p>
<p>7、小红鞋</p>
<p><img class="blogimg" border="0" small="0" src="http://hiphotos.baidu.com/li%5Fzhongnan/pic/item/2507c0d928b69cf338012fe9.jpg"></p> <a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/9f13003e19b6fdfc838b1332.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/li%5Fzhongnan/blog/category/%CF%D0%BB%B0%D0%C4%C7%E9">闲话心情</a>&nbsp;<a href="http://hi.baidu.com/li%5Fzhongnan/blog/item/9f13003e19b6fdfc838b1332.html#comment">查看评论</a>]]></description>
        <pubDate>2008年10月06日 星期一  21:41</pubDate>
        <category><![CDATA[闲话心情]]></category>
        <author><![CDATA[li_zhongnan]]></author>
		<guid>http://hi.baidu.com/li%5Fzhongnan/blog/item/9f13003e19b6fdfc838b1332.html</guid>
</item>


</channel>
</rss>