Derby源代碼分析 -- 服務器啟動

作者: zddava  來源: JavaEye  發布時間: 2010-11-08 21:26  閱讀: 2723 次  推薦: 0   原文鏈接   [收藏]  

  一直以來都想研究下數據庫的實現原理,Derby在我眼里絕對是一個完美的切入點。首先它是100%純Java實現,對于我這種Java程序員來說簡直完美的,不需要去懂C,C++...等等無疑減少了很大的難度;其次,它是開源的,當然這個就是廢話,不然我去哪里弄它的源碼去。廢話少說,首先簡單的介紹下Derby。

  Derby的啟動方式有兩種,一種是網絡服務器的方式,這種方式就和大家平時用的Oracle,SQL Server,DB2等等沒什么區別,另外一種是嵌入式方式,是一種類似于Access的方式,在這種方式下,Derby與應用程序運行于同一個JVM下,隨著應用程序的關閉而關閉(我很欣賞這個方式,呵呵)。另外,Java6中包含的Java DB其實就是Derby。具體的可以google下Derby,會有更加詳細的介紹,我這里就不贅述了。

好了,簡單的介紹后,來轉入正題,看一下Derby服務器的啟動上。

  1.首先來了解下網絡服務器的方式

  Derby有幾種啟動方式,其中一種就是用bin目錄下的startNetworkServer.bat這個腳本文件。可以大致的看一下它,可以發現Derby網絡服務器方式是在org.apache.derby.drda.NetworkServerControl類啟動的,并且為命令行傳入一個參數start。好了,下面就可以以這個類為起點來看Derby的啟動部分了。根據我對Derby源代碼的大致理解,Derby的網絡服務器方式的啟動所涉及的類大致如下圖: 下面首先來看看NetworkServerControl的源代碼吧,

 

 
public static void main(String args[]) {
NetworkServerControlImpl server
= null;
boolean printErrors = true;
try {
server
= new NetworkServerControlImpl();

int command = server.parseArgs(args);
if (needsSecurityManager(server, command)) {
verifySecurityState(server);
installSecurityManager(server);
}
printErrors
= false;
/* 執行命令 */
server.executeWork(command);
}
catch (Exception e) {
if ((e.getMessage() == null) || !e.getMessage().equals(NetworkServerCon
trolImpl.UNEXPECTED_ERR)
|| printErrors) {
if (server != null)
server.consoleExceptionPrint(e);

else
e.printStackTrace();
}
System.exit(
1);
}
System.exit(
0);
}

  這段代碼沒有什么復雜的,最主要的不服是調用了server的executeWork()方法,其中server是一個NetworkServerControlImpl的實例。 executeWork()方法主要是對各種不同的參數進行不同的調用,由于我們是看的啟動部分,所以就只看一部分就可以了。

 

 

 
public void executeWork(int command) throws Exception {
if (command == COMMAND_UNKNOWN)
return;

if (commandArgs.size() != COMMAND_ARGS[command])
consolePropertyMessage(
"DRDA_InvalidNoArgs.U", COMMANDS[command]);
int min;
int max;

switch (command) {
case COMMAND_START:
shutdownDatabasesOnShutdown
= true;
/* 開啟網絡服務 */
blockingStart(makePrintWriter(System.out));
break;
case COMMAND_SHUTDOWN:

......

}
}

  這里沒什么可說的,主要就是要看這個blockStart()方法了,這個方法比較長,我把一些異常處理的部分去掉了

 

 

 
public void blockingStart(PrintWriter consoleWriter) throws Exception {
/* 1. 載入Derby的Driver */
startNetworkServer();
setLogWriter(consoleWriter);
cloudscapeLogWriter
= Monitor.getStream().getPrintWriter();
if (SanityManager.DEBUG && debugOutput) {
memCheck.showmem();
mc
= new memCheck(200000);
mc.start();
}


/* 2. 打開一個ServerSocket */
try {
serverSocket
= (ServerSocket) AccessController
.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws IOException {
return createServerSocket();
}
});
}
catch (PrivilegedActionException e) {
......
}
catch (Exception e) {
......
}


switch (getSSLMode()) {
default:
case SSL_OFF:
consolePropertyMessage(
"DRDA_Ready.I", new String[] { Integer.toString(portNumber), att_srvclsnm, versionString });
break;
case SSL_BASIC:
consolePropertyMessage(
"DRDA_SSLReady.I", new String[] { Integer.toString(portNumber), att_srvclsnm, versionString });
break;
case SSL_PEER_AUTHENTICATION:
consolePropertyMessage(
"DRDA_SSLClientAuthReady.I", new String[] { Integer.toString(portNumber), att_srvclsnm, versionString });
break;
}


/* 3. 新開一個ClientThread去接受用戶請求 */
final ClientThread clientThread = (ClientThread) AccessController
.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws Exception {
return new ClientThread(thisControl, serverSocket);
}
});
clientThread.start();

ManagementService mgmtService
= ((ManagementService) Monitor.getSystemModule(Module.JMX));

final Object versionMBean = mgmtService.registerMBean(new Version(getNetProductVersionHolder(), SystemPermission.SERVER), VersionMBean.class, "type=Version,jar=derbynet.jar");
final Object networkServerMBean = mgmtService.registerMBean(new NetworkServerMBeanImpl(this), NetworkServerMBean.class,"type=NetworkServer");

/* 直到shutdown或者出現InterruptedException,否則一直wait() */
synchronized (shutdownSync) {
try {
shutdownSync.wait();
}
catch (InterruptedException e) {
shutdown
= true;
}
}

AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
if (mc != null)
mc.interrupt();

clientThread.interrupt();

return null;
}
});


/* 關閉Session連接 */
synchronized (sessionTable) {
for (Enumeration e = sessionTable.elements(); e.hasMoreElements();) {
Session session
= (Session) e.nextElement();
session.close();
}
}


/* 關閉DRDA協議客戶端連接 */
synchronized (threadList) {
for (int i = 0; i < threadList.size(); i++) {
final DRDAConnThread threadi = (DRDAConnThread) threadList.get(i);
threadi.close();
AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
threadi.interrupt();

return null;
}
});
}
threadList.clear();
}


/* 關閉ServerSocket */
try {
serverSocket.close();
}
catch (IOException e) {
consolePropertyMessage(
"DRDA_ListenerClose.S", true);
}


/* 喚醒等待中的線程 */
synchronized (runQueue) {
runQueue.notifyAll();
}

mgmtService.unregisterMBean(versionMBean);
mgmtService.unregisterMBean(networkServerMBean);


/* 關閉Derby */
if (shutdownDatabasesOnShutdown) {
try {
if (cloudscapeDriver != null) {
final Properties p = new Properties();
if (userArg != null) {
p.setProperty(
"user", userArg);
}

if (passwordArg != null) {
p.setProperty(
"password", passwordArg);
}
cloudscapeDriver.connect(
"jdbc:derby:;shutdown=true", p);
}
}
catch (SQLException sqle) {
......
}
}

consolePropertyMessage(
"DRDA_ShutdownSuccess.I", new String[] { att_srvclsnm, versionString });
}

 

  這段代碼有3個地方需要仔細研究下,分別在上述代碼中用注釋標注的1,2,3處。首先來看一下第1處,就是Derby驅動的載入,

 

 
private ServerSocket createServerSocket() throws IOException {
if (hostAddress == null)
hostAddress
= InetAddress.getByName(hostArg);
// 創建本地地址列表
buildLocalAddressList(hostAddress);

// 根據不同的情況來創建Socket
switch (getSSLMode()) {
case SSL_OFF:
default:
ServerSocketFactory sf
= ServerSocketFactory.getDefault();
return sf.createServerSocket(portNumber, 0, hostAddress);
case SSL_BASIC:
SSLServerSocketFactory ssf
= (SSLServerSocketFactory) SSLServerSocketFactory
.getDefault();

return (SSLServerSocket) ssf.createServerSocket(portNumber, 0,
hostAddress);

case SSL_PEER_AUTHENTICATION:
SSLServerSocketFactory ssf2
= (SSLServerSocketFactory) SSLServerSocketFactory
.getDefault();
SSLServerSocket sss2
= (SSLServerSocket) ssf2.createServerSocket(
portNumber,
0, hostAddress);
sss2.setNeedClientAuth(
true);
return sss2;
}
}

  最后一個部分是新開一個ClientThread去接受用戶請求,由于ClientThread是一個線程類,可以看下它的#run()的實現。

 

 

 
public void run() {
Socket clientSocket
= null;

for (;;) { // 用一個死循環去不斷的處理客戶端連接

try { // 捕獲所有異常的try

try { // 捕獲InterruptedException,SSLException和IOException

try { // 捕獲PrivilegedActionException
/* 從ServerSocket獲得Socket */
clientSocket = (Socket) AccessController
.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws IOException {
return serverSocket.accept();
}
});

/* 判斷服務器是否關閉了 */
if (parent.getShutdown()) {
clientSocket.close();

return;
}

/* 設定屬性 */
clientSocket.setKeepAlive(parent.getKeepAlive());

if (timeSlice > 0)
clientSocket.setSoTimeout(timeSlice);


/* 加入Session隊列 */
parent.addSession(clientSocket);

}
catch (PrivilegedActionException e) {
throw e.getException();
}

}
catch (InterruptedException ie) {
return;

}
catch (javax.net.ssl.SSLException ssle) {
parent.consoleExceptionPrintTrace(ssle);
parent.directShutdownInternal();

return;

}
catch (IOException ioe) {
synchronized (parent.getShutdownSync()) {
if (!parent.getShutdown()) {
parent.consoleExceptionPrintTrace(ioe);

if (clientSocket != null)
clientSocket.close();
}
}

return;
}
}
catch (Exception e) {
parent.consoleExceptionPrintTrace(e);

try {
if (clientSocket != null)
clientSocket.close();
}
catch (IOException closeioe) {
parent.consoleExceptionPrintTrace(closeioe);
}
}

}

}

  這樣,當有新的Socket請求到了服務器時,會把這個請求加入到NetworkServerControlImpl的Session隊列中等待處理。

 

0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()