QuickServer開(kāi)發(fā)指南(1)- 介紹?
QuickServer是一個(gè)免費(fèi)的開(kāi)源Java庫(kù),用于快速創(chuàng)建健壯的多線程、多客戶端TCP服務(wù)器應(yīng)用程序。使用QuickServer,用戶可以只集中處理應(yīng)用程序的邏輯/協(xié)議,從而方便的建立功能強(qiáng)大的服務(wù)器應(yīng)用。該程序由Akshathkumar Shetty設(shè)計(jì)和實(shí)現(xiàn)。
??? QuickServer安裝目錄下的example中有演示其功能的例子,最新的例子和文檔可以通過(guò)網(wǎng)站 http://www.quickserver.org或http://quickserver.sourceforge.net獲得。
??? 該指南適用于所有想要學(xué)習(xí)和使用QuickServer的人,閱讀該指南應(yīng)具備基本的Java編程知識(shí),基本的網(wǎng)絡(luò)和sockets方面的知識(shí)也會(huì)有所幫助
1.?為什么需要QuickServer?
??? 無(wú)論何種編程語(yǔ)言,socket編程對(duì)程序員來(lái)說(shuō)都不是一件容易的事,創(chuàng)建多線程、多客戶端的服務(wù)器socket更像一場(chǎng)惡夢(mèng)了。在每個(gè)新的軟件中處理多socket連接,我們都要浪費(fèi)大把時(shí)間編寫大量重復(fù)的代碼。QuickServer因而誕生——使用Java創(chuàng)建多線程、多客戶端服務(wù)器應(yīng)用。
2.?基本構(gòu)造
QuickServer在應(yīng)用邏輯上為開(kāi)發(fā)者提供了四個(gè)類
o?ClientCommandHandler
??? 處理與客戶端的交互——使用字符串命令
o?ClientObjectHandler [可選類]
??? 處理與客戶端的交互——使用對(duì)象命令
o?Authenticator [可選類]
??? 客戶端驗(yàn)證
o?ClientData [可選類]
??? 客戶端數(shù)據(jù)載體(支持類)
下面的圖表顯示了QuickServer庫(kù)的基本構(gòu)造。QuickServer模塊上七個(gè)輻條表示七個(gè)方法:
o?java.lang.String info()
o?int getServiceState()
o?boolean initService(java.lang.Object[] config)
o?boolean startService()
o?boolean resumeService()
o?boolean suspendService()
o?boolean stopService()
??? 與QuickServer模塊相連接的四個(gè)組件中只有ClientCommandHandler是必須的。
??? QuickServerConfig對(duì)象由initService()方法構(gòu)建。它實(shí)現(xiàn)了QuickServer,在讀取XML配置后,QuickServerConfig用于QuickServer配置。
??? ClientHandler線程對(duì)象用于客戶端緩沖池??蛇x的ClientData類與ClientHandler類關(guān)聯(lián),ClientHandler對(duì)象容器參考ClientCommandHandler,ClientObjectHandler(可選),Authenticator(可選)對(duì)象包含在QuickServer主函數(shù)中。
??? 注意:上圖中并未顯示QSAdminServer,它是圖中QuickServer的組成部分。
3.?主要特點(diǎn)
o?創(chuàng)建多線程、多客戶端TCP服務(wù)器應(yīng)用程序
o?支持安全服務(wù)的創(chuàng)建:SSL, TLS
o?清楚的分離服務(wù)、協(xié)議、驗(yàn)證邏輯
o?GUI圖形界面遠(yuǎn)程管理支持
o?Command Shell對(duì)服務(wù)器的本地管理
o?無(wú)須斷開(kāi)客戶端連接的重啟或延遲服務(wù)
o?為線程的再利用和大多數(shù)的使用對(duì)象建立緩沖池
o?完全的日志支持(Java構(gòu)建)
o?支持發(fā)送和接收字符串、字節(jié)、二進(jìn)制、序列化Java對(duì)象
o?在同樣的xml中支持能夠存貯指定應(yīng)用數(shù)據(jù)的XML配置
o?支持通過(guò)IP地址限制服務(wù)
o?支持基于XML的JDBC映射
o?支持服務(wù)配置模式
o?支持從xml加載/重新加載用于jar包
o?在QuickServer中添加處理hooks
o?指定允許的最大客戶端連接數(shù)
o?在通常的TCP連接上支持談判安全連接
o?支持鑒別和查詢客戶端
o?附帶典型例子——FTPServer, CmdServer,EchoWebServer, ChatServer
4.?1.4版的新功能
o?為QuickServer添加安全模式:SSL, TLS
o?添加SecureManagerLoader管理安全模式
o?在通常的TCP連接上添加談判安全連接
o?添加初始化服務(wù)hooks
o?為通信添加二進(jìn)制模式
o?為QsAdminServer通信添加QSAdminAPI
o?為QuickServer 添加findAllClientByKey
o?添加ConnectionLostException類
o?改進(jìn)ClientHandler、安全配置
o?新例子——XmlAdder:一個(gè)簡(jiǎn)單的xml服務(wù),可添加兩個(gè)整數(shù)
o?新例子——PipeServer:一個(gè)簡(jiǎn)單的重定向服務(wù)
QuickServer開(kāi)發(fā)指南(2)- 安裝
1. 運(yùn)行環(huán)境
QuickServer 1.2以上的版本需要(其實(shí)在偶看來(lái)一個(gè)1.4版以上JDK足矣):
? 推薦1.4版以上Java虛擬機(jī),最低1.3版(未經(jīng)測(cè)試).
? Java Logging API(下列之一)
o java.util.logging包 [JDK 1.4版自帶]
o Lumberjack庫(kù) [http://javalogging.sourceforge.net/]
? XML 解析器 (下列之一)
o SAX (面向XML 2.0的API) [JDK 1.4版自帶]
o JAXP (面向XML解析的Java API) 1.1 [JDK 1.4版自帶]
o Xerces [http://xml.apache.org/xerces2-j]
o Crimson [http://xml.apache.org/crimson]
? Jakarta公共組件{Digester, Pool}
o 這些產(chǎn)品包含在Apache開(kāi)發(fā)的軟件中(http://www.apache.org/)。Jar包都在以下的庫(kù)中:BeanUtils, Collections, Logging. [http://jakarta.apache.org/commons/components.html]. Apache軟件許可證在文件“apache_license.txt”中。
2. 安裝
??? 目前最新的1.4.1版QuickServer可在http://www.quickserver.org/download.html下載。安裝QuickServer,假設(shè)安裝路徑為$INSTALL_PATH。
??? 在CLASSPATH中添加"$INSTALL_PATH\dist\QuickServer.jar",在PATH中添加"$INSTALL_PATH\bin"。
??? 另外測(cè)試socket的通訊軟件推薦SockTest,在http://www.ddost.com/soft/sockettest 可下載到最新版本。Windows自帶的telnet也可以進(jìn)行測(cè)試。
QuickServer開(kāi)發(fā)指南(3)- 構(gòu)建EchoServer?
學(xué)習(xí)怎樣使用QuickServer庫(kù)的一個(gè)好的方法是學(xué)習(xí)它提供的例子。在QuickServer安裝路徑下的examples文件夾里有許多典型的例子。
??? 下面的章節(jié)里我們模仿其中的一個(gè)例子EchoServer來(lái)構(gòu)建一個(gè)服務(wù)器。EchoServer是一個(gè)簡(jiǎn)單的TCP服務(wù)器,主要功能是將用戶發(fā)送的字符串加上前綴"Echo :"后返回。雖然這個(gè)例子可用性不強(qiáng),但它是一個(gè)對(duì)QuickServer所有特點(diǎn)的一個(gè)很好的示范。我們從構(gòu)建一個(gè)最基本的服務(wù)器開(kāi)始,以后慢慢給它添加新的功能。
1.?代碼
??? 首先實(shí)現(xiàn)EchoServer最基本的功能:將用戶發(fā)送的字符串加上前綴"Echo :"后返回。
??? 在本地創(chuàng)建一個(gè)文件夾存放需要的代碼,如在c:\projects\中建立echoserver文件夾,然后創(chuàng)建一個(gè)類EchoServer.java:
01 package echoserver; 02 03 import org.quickserver.net.*; 04 import org.quickserver.net.server.*; 05 06 import java.io.*; 07 08 public class EchoServer { 09 public static void main(String s[]) { 10 QuickServer myServer = 11 new QuickServer("echoserver.EchoCommandHandler"); 12 myServer.setPort(4123); 13 myServer.setName("EchoServer v 1.0"); 14 try { 15 myServer.startServer(); 16 } catch(AppException e){ 17 System.err.println("Error in server : "+e); 18 } 19 } 20 } |
??? 在第10行和第11行定義了一個(gè)QuickServer對(duì)象myServer,通過(guò)一個(gè)String對(duì)象"echoserver.EchoCommandHandler"聲明了要加載的類,這個(gè)類面向所有客戶端做命令處理器,實(shí)現(xiàn)了org.quickserver.net.server.ClientCommandHandler接口,我們即將創(chuàng)建。
??? 第12行設(shè)置了一個(gè)服務(wù)器端口用來(lái)做監(jiān)聽(tīng),然后設(shè)置整個(gè)應(yīng)用的名字(第13行)。最后啟動(dòng)服務(wù)(第15行)。
??? 接下來(lái)為EchoServer創(chuàng)建一個(gè)實(shí)現(xiàn)org.quickserver.net.server.ClientCommandHandler接口的類EchoCommandHandler.java,用來(lái)處理服務(wù)器發(fā)送的命令。
01 // EchoCommandHandler.java 02 package echoserver; 03 04 import java.net.*; 05 import java.io.*; 06 import org.quickserver.net.server.ClientCommandHandler; 07 import org.quickserver.net.server.ClientHandler; 08 09 public class EchoCommandHandler implements ClientCommandHandler { 10 11 public void gotConnected(ClientHandler handler) 12 throws SocketTimeoutException, IOException { 13 handler.sendClientMsg("+++++++++++++++++++++++++++++++"); 14 handler.sendClientMsg("| Welcome to EchoServer v 1.3 |"); 15 handler.sendClientMsg("| Send 'Quit' to exit |"); 16 handler.sendClientMsg("+++++++++++++++++++++++++++++++"); 17 } 18 public void lostConnection(ClientHandler handler) 19 throws IOException { 20 handler.sendSystemMsg("Connection lost : " + 21 handler.getSocket().getInetAddress()); 22 } 23 public void closingConnection(ClientHandler handler) 24 throws IOException { 25 handler.sendSystemMsg("Closing connection : " + 26 handler.getSocket().getInetAddress()); 27 } 28 29 public void handleCommand(ClientHandler handler, String command) 30 throws SocketTimeoutException, IOException { 31 if(command.equals("Quit")) { 32 handler.sendClientMsg("Bye ;-)"); 33 handler.closeConnection(); 34 } else { 35 handler.sendClientMsg("Echo : "+command); 36 } 37 } 38 } |
??? 根據(jù)QuickServer的要求,這個(gè)類必須實(shí)現(xiàn)ClientCommandHandler接口。
??? 當(dāng)客戶端建立一個(gè)連接(11行),gotConnected()方法被調(diào)用。在這個(gè)方法里面,我們給客戶端發(fā)送歡迎文本(13-16行),這些文本使用通過(guò)ClientHandler的sendClientMsg()方法發(fā)送給客戶端。我們也會(huì)使用ClientHandler的sendSystemMessage()方法顯示客戶端連接的InetAddress(20-21,25-26行)。
??? handlerCommand()方法是ClientCommandHandler接口的核心方法,因?yàn)榉?wù)器接收客戶端發(fā)送的任何命令時(shí)都要調(diào)用該方法。在我們對(duì)這個(gè)方法的實(shí)現(xiàn)中,我們會(huì)檢查命令是否為"Quit"(31行),如果是,我們將發(fā)送一些提示文本表示服務(wù)器即將關(guān)閉連接,然后關(guān)閉連接(33行)。否則,將命令加上前綴"Echo :"返回給用戶。
2.?運(yùn)行和測(cè)試
o?運(yùn)行命令提示符程序(cmd.exe)
o?進(jìn)入代碼所在文件夾根目錄,如c:\projects
o?編譯代碼? javac echoserver\*.java
o?若無(wú)編譯錯(cuò)誤,運(yùn)行服務(wù)器:
??? set classpath=%classpath%;d:\QuickServer\dist\QuickServer.jar;.\(類所在文件夾)
??? java echoserver.EchoServer
o?您將會(huì)看到如下信息:
o?測(cè)試我們的服務(wù)器是否可以正常工作。再運(yùn)行一個(gè)cmd程序,進(jìn)入SocketTest.jar所在目錄,鍵入java -jar sockettest.jar命令,彈出一個(gè)窗口。在IP Address中輸入"127.0.0.1",在Port里輸入"4123",點(diǎn)擊"Connect"按鈕,將看到窗口中顯示如下圖的信息。
??? 若使用telnet,可鍵入命令:open localhost 4123
??? 在Message中輸入一些字符串,點(diǎn)擊"Send"按鈕,瀏覽器將會(huì)返回一個(gè)加了前綴"Echo :"的字符串。發(fā)送"Quit",服務(wù)器斷開(kāi)連接。
QuickServer開(kāi)發(fā)指南(4)- 添加認(rèn)證
現(xiàn)在我們給剛剛創(chuàng)建的服務(wù)器添加認(rèn)證功能。
??? 查看org.quickserver.net.server.QuickServer的文檔(docs文件夾下)你可以注意到里面有一個(gè)方法
??? public void setAuthenticator(java.lang.String authenticator)
??? 閱讀文檔可知此方法中的authenticator字符串是實(shí)現(xiàn)org.quickserver.net.server.Authenticator接口的方法的全名。
??? Authenticator接口有兩個(gè)實(shí)現(xiàn):
??? org.quickserver.net.server.QuickAuthenticator:這個(gè)類用來(lái)驗(yàn)證連接QuickServer的客戶端。它只用一個(gè)實(shí)例處理所有的QuickServer驗(yàn)證。(推薦)
??? org.quickserver.net.server.ServerAuthenticator:這個(gè)類同樣用來(lái)驗(yàn)證連接QuickServer的客戶端,但對(duì)每一個(gè)驗(yàn)證的處理都會(huì)創(chuàng)建一個(gè)實(shí)例。
??? 接下來(lái)給EchoServer加驗(yàn)證功能。簡(jiǎn)單點(diǎn),客戶端輸入的用戶名和密碼一致就算驗(yàn)證通過(guò)。
??? 首先,在同樣的文件夾里創(chuàng)建一個(gè)驗(yàn)證類:EchoServerQuickAuthenticator
01 package echoserver; 02 03 import org.quickserver.net.server.*; 04 import java.io.*; 05 06 public class EchoServerQuickAuthenticator extends QuickAuthenticator { 07 08 public boolean askAuthorisation(ClientHandler clientHandler) 09 throws IOException { 10 String username = askStringInput(clientHandler, "User Name :"); 11 String password = askStringInput(clientHandler, "Password :"); 12 13 if(username==null || password ==null) 14 return false; 15 16 if(username.equals(password)) { 17 sendString(clientHandler, "Auth OK"); 18 return true; 19 } else { 20 sendString(clientHandler, "Auth Failed"); 21 return false; 22 } 23 } 24 } |
??? 接下來(lái)我們要告訴QuickServer使用我們新創(chuàng)建的驗(yàn)證類來(lái)做驗(yàn)證器。修改前一章創(chuàng)建的EchoServer.java文件,代碼如下(粗體為修改的代碼):
01 package echoserver; 02 03 import com.ddost.net.*; 04 import com.ddost.net.server.*; 05 06 import java.io.*; 07 08 public class EchoServer { 09 10 public static void main(String s[]) { 11 12 QuickServer myServer = 13 new QuickServer("echoserver.EchoCommandHandler"); 14myServer.setAuthenticator( 15"echoserver.EchoServerQuickAuthenticator"); 16 myServer.setPort(4123); 17 myServer.setName("EchoServer v 1.0"); 18try { 19 myServer.startServer(); 20 } catch(AppException e){ 21 System.err.println("Error in server : "+e); 22 } 23 } 24 } |
??? OK,將修改好的文件編譯,按照前一章講述的方法運(yùn)行程序。這次當(dāng)我們點(diǎn)擊"Connect"時(shí),瀏覽器會(huì)要求我們輸入用戶名和密碼。如果輸入的用戶名和密碼一致就可以登錄。如果輸入錯(cuò)誤五次以上,瀏覽器會(huì)提示"-ERR Max Auth Try Reached"并自動(dòng)斷開(kāi)連接。這個(gè)次數(shù)和提示信息可以通過(guò)QuickServer類的setMaxAuthTry() 和 setMaxAuthTryMsg()修改。
??? 有時(shí)在驗(yàn)證過(guò)程中我們可能需要中途退出而不是等待驗(yàn)證結(jié)束,這時(shí)輸入"Quit"是不起作用的。我們可以這樣修改代碼,有兩個(gè)方法:
??? 一是從EchoServerQuickAuthenticator類中的askAuthorisation()方法拋出一個(gè)org.quickserver.net.AppException異常,代碼如下:
??? String username = askStringInput(clientHandler, "User Name :");
??? if (username != null &&
??????? username.equalsIgnoreCase("QUIT")) {
????? sendString(clientHandler, "Logged out.");
????? throw new AppException("Quit");
}
??? 或者參考ClientHandler,關(guān)閉連接,代碼如下:
??? String username = askStringInput(clientHandler, "User Name :");
??? if (username != null &&
??????? username.equalsIgnoreCase("QUIT")) {
????? sendString(clientHandler, "Logged out.");
????? clientHandler.closeConnection();
????? return false;
}
??? ClientHandler對(duì)象能夠提供很多客戶端連接的有用信息,如IP地址。更多信息請(qǐng)參考API文檔。
注意:
??? o 不要在驗(yàn)證器類中存貯任何客戶端相關(guān)信息,如果需要,必須存放在ClientData類中--下一章將講解該部分內(nèi)容。
??? o 必須確認(rèn)askAuthorisation()方法是線程安全的。
QuickServer開(kāi)發(fā)指南(5)- 客戶數(shù)據(jù)
既然不能在ClientCommandHandler和ServerAuthenticator類中保存客戶數(shù)據(jù),我們使用ClientData類的handleCommand()或askAuthorisation()方法來(lái)存儲(chǔ)所有的客戶端信息。
??? 示范一下這個(gè)特點(diǎn)有什么用。還是以EchoServer為例,當(dāng)用戶發(fā)送"Hello"時(shí),我們給他一個(gè)問(wèn)候。如果用戶再發(fā)送"Hello",我們提醒他已經(jīng)發(fā)了n次"Hello"。接下來(lái)定義ClientData類來(lái)存儲(chǔ)用戶名以及他向服務(wù)器發(fā)送"Hello"的次數(shù)。
1.?代碼
1.?在EchoServer中創(chuàng)建一個(gè)EchoServerData類
01 //---- EchoServerData.java ---- 02?package echoserver; 03 04 import org.quickserver.net.server.*; 05 import java.io.*; 06 07 public class EchoServerData implements ClientData { 08 private int helloCount; 09 private String username; 10 11 public void setHelloCount(int count) { 12 helloCount = count; 13 } 14 public int getHelloCount() { 15 return helloCount; 16 } 17 18 public void setUsername(String username) { 19 this.username = username; 20 } 21 public String getUsername() { 22 return username; 23 } 24 } 25 //--- end of code --- |
2.?告訴QuickServer用這個(gè)EchoServerData來(lái)做為它的ClientData類。
??? 修改前面創(chuàng)建的EchoServer.java,代碼如下:
01 package echoserver; 02 03 import org.quickserver.net.*; 04 import org.quickserver.net.server.*; 05 06 import java.io.*; 07 08 public class EchoServer { 09 public static void main(String s[]) { 10 11 String cmd = "echoserver.EchoCommandHandler"; 12 String auth = "echoserver.EchoServerQuickAuthenticator"; 13 String data = "echoserver.EchoServerData"; 14 15 QuickServer myServer = new QuickServer(cmd); 16 myServer.setAuthenticator(auth); 17 myServer.setClientData(data); 18 19 myServer.setPort(4123); 20 myServer.setName("Echo Server v 1.0"); 21 try { 22 myServer.startServer(); 23 } catch(AppException e){ 24 System.out.println("Error in server : "+e); 25 } 26 } 27 } |
??? 上面的代碼中,我們將配置信息寫入String對(duì)象來(lái)設(shè)置QuickServer。???
3.?修改Authenticator類,也就是EchoServerAuthenticator類,讓它在ClientData對(duì)象中存儲(chǔ)用戶名。下面是修改后的代碼:
01 package echoserver; 02 03 import org.quickserver.net.server.*; 04 import java.io.*; 05 06 public class EchoServerQuickAuthenticator extends QuickAuthenticator { 07 08 public boolean askAuthorisation(ClientHandler clientHandler) 09 throws IOException { 10 String username = askStringInput(clientHandler, "User Name :"); 11 if(username!=null && username.equalsIgnoreCase("QUIT")) { 12 sendString(clientHandler, "Logged out."); 13 //close the connection 14 clientHandler.closeConnection(); 15 return false; 16 } 17 18 String password = askStringInput(clientHandler, "Password :"); 19 20 if(username==null || password ==null) 21 return false; 22 23 if(username.equals(password)) { 24 sendString(clientHandler, "Auth OK"); 25 //store the username in ClientData 26 EchoServerData data = (EchoServerData)clientHandler.getClientData(); 27 data.setUsername(username); 28 return true; 29 } else { 30 sendString(clientHandler, "Auth Failed"); 31 return false; 32 } 33 } 01 // EchoCommandHandler.java 02 package echoserver; 03 04 import java.net.*; 05 import java.io.*; 06 import org.quickserver.net.server.ClientCommandHandler; 07 import org.quickserver.net.server.ClientHandler; 08 09 public class EchoCommandHandler implements ClientCommandHandler { 10 11 public void gotConnected(ClientHandler handler) 12 throws SocketTimeoutException, IOException { 13 handler.sendClientMsg("+++++++++++++++++++++++++++++++"); 14 handler.sendClientMsg("| Welcome to EchoServer v 1.0 |"); 15 handler.sendClientMsg("| Note: Password = Username |"); 16 handler.sendClientMsg("| Send 'Quit' to exit |"); 17 handler.sendClientMsg("+++++++++++++++++++++++++++++++"); 18 } 19 public void lostConnection(ClientHandler handler) 20 throws IOException { 21 handler.sendSystemMsg("Connection lost : " + 22 handler.getSocket().getInetAddress()); 23 } 24 public void closingConnection(ClientHandler handler) 25 throws IOException { 26 handler.sendSystemMsg("Closing connection : " + 27 handler.getSocket().getInetAddress()); 28 } 29 30 public void handleCommand(ClientHandler handler, String command) 31 throws SocketTimeoutException, IOException { 32 if(command.equals("Quit")) { 33 handler.sendClientMsg("Bye ;-)"); 34 handler.closeConnection(); 35 } if(command.equalsIgnoreCase("hello")) { 36 EchoServerData data = (EchoServerData) handler.getClientData(); 37 data.setHelloCount(data.getHelloCount()+1); 38 if(data.getHelloCount()==1) { 39 handler.sendClientMsg("Hello "+data.getUsername()); 40 } else { 41 handler.sendClientMsg("You told Hello "+data.getHelloCount()+ 42 " times. "); 43 } 44 } else { 45 handler.sendClientMsg("Echo : "+command); 46 } 47 } 48 }
4.?修改ClientCommandHandler實(shí)現(xiàn)類EchoCommandHandler。如果用戶發(fā)送"Hello",給他一個(gè)問(wèn)候。如果他發(fā)送多次"Hello",告訴他已經(jīng)發(fā)送了n次"Hello"。下面是修改后的代碼:
5.?編譯改好的程序,運(yùn)行,使用SocketTest測(cè)試。登錄后,發(fā)送"Hello",系統(tǒng)會(huì)給一個(gè)問(wèn)候,再次發(fā)送"Hello",它將告訴你發(fā)送了多少次"Hello"。
?
4.2?創(chuàng)建ClientData池
??? 現(xiàn)在我們知道ClientData可以正常工作了。但是對(duì)每一個(gè)連接QuickServer的客戶端都要?jiǎng)?chuàng)建一個(gè)新的ClientData對(duì)象,可能會(huì)造成性能上的瓶頸,尤其對(duì)性能要求較高的服務(wù)器來(lái)說(shuō)。
??? 我們可以創(chuàng)建一個(gè)ClientData池對(duì)象,無(wú)論客戶端什么時(shí)候進(jìn)行連接,都使用同一個(gè)對(duì)象。首先實(shí)現(xiàn)下面的接口:
??? org.quickserver.util.pool.PoolableObject
??? 查找QuickServer API文檔可以發(fā)現(xiàn)PoolableObject只有兩個(gè)必須實(shí)現(xiàn)的方法:
??? org.apache.commons.pool.PoolableObjectFactory屬于通常的工廠方法。
??? isPoolable()判斷對(duì)象是否可以成為池對(duì)象。
PoolableObjectFactory
org.apache.commons.pool.PoolableObjectFactory接口包含了以下方法:
o?void activateObject(Object obj):重新初始化一個(gè)實(shí)例。
o?void destroyObject(Object obj):銷毀一個(gè)不再需要的實(shí)例。
o?Object makeObject():創(chuàng)建一個(gè)實(shí)例。
o?void passivateObject(Object obj):禁止初始化一個(gè)實(shí)例。
o?boolean validateObject(Object obj):確定一個(gè)實(shí)例是否安全。
??? 我們可以擴(kuò)展一個(gè)基于無(wú)操作的實(shí)現(xiàn)來(lái)創(chuàng)建可"池"化的對(duì)象:
??? org.apache.commons.pool.BasePoolableObjectFactory
??? 這個(gè)類只有一個(gè)抽象方法makeObject()和一個(gè)validateObject()方法,它只返回true。
??? 我們來(lái)創(chuàng)建一個(gè)EchoServerPoolableData類。
01 //---- EchoServerPoolableData.java ---- 02 package echoserver; 03 04 import org.quickserver.net.server.*; 05 import java.io.*; 06 07 public class EchoServerPoolableData 08 extends EchoServerData 09 implements org.apache.commons.pool.PoolableObjectFactory { 10 11 public void activateObject(Object obj) { 12 } 13 public void destroyObject(Object obj) { 14 if(obj==null) return; 15 passivateObject(obj); 16 obj = null; 17 } 18 public Object makeObject() { 19 return new EchoServerPoolableData(); 20 } 21 public void passivateObject(Object obj) { 22 EchoServerPoolableData pd = (EchoServerPoolableData)obj; 23 pd.setHelloCount(0); 24 pd.setUsername(null); 25 } 26 public boolean validateObject(Object obj) { 27 if(obj==null) 28 return false; 29 else 30 return true; 31 } 32 }
??? 這個(gè)類擴(kuò)展了我們的EchoServerData,然后我們實(shí)現(xiàn)了org.apache.commons.pool.BasePoolableObjectFactory,這個(gè)實(shí)現(xiàn)是簡(jiǎn)單的不需要解釋了。
??? 現(xiàn)在我們需要告訴QuickServer使用這個(gè)類來(lái)代替原來(lái)的ClientData類。
??????? myServer.setClientData("echoserver.EchoServerPoolableData");
??? 編譯修改的程序,可能會(huì)報(bào)如下錯(cuò)誤:
??????? package org.apache.commons.pool does not exist。
??? 這是因?yàn)榫幾g器不知道這個(gè)類??梢栽诃h(huán)境變量中添加D:\QuickServer\dist\commons-pool.jar包,并在運(yùn)行時(shí)
??? set classpath=%classpath%;d:\QuickServer\dist\QuickServer.jar; d:\QuickServer\dist\commons-pool.jar;.\(類所在文件夾)即可。